Skip to content
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

Clear sensitive memory without getting optimized out (revival of #636) #1579

Merged

Conversation

theStack
Copy link
Contributor

@theStack theStack commented Aug 6, 2024

This PR picks up #636 (which in turn picked up #448, so this is take number three) and is essentially a rebase on master.

Some changes to the original PR:

So far I haven't looked at any disassembly and possible performance implications yet (there were some concerns expressed in #636 (comment)), happy to go deeper there if this gets Concept ACKed.

The proposed method of using a memory barrier to prevent optimizating away the memset is still used in BoringSSL (where it was originally picked up from) and in the Linux Kernel, see e.g. https://github.com/google/boringssl/blob/5af122c3dfc163b5d1859f1f450756e8e320a142/crypto/mem.c#L335 and https://github.com/torvalds/linux/blob/d4560686726f7a357922f300fc81f5964be8df04/include/linux/string.h#L348 / https://github.com/torvalds/linux/blob/d4560686726f7a357922f300fc81f5964be8df04/include/linux/compiler.h#L102

Fixes #185.

Copy link
Contributor

@real-or-random real-or-random left a comment

Choose a reason for hiding this comment

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

Concept ACK (obviously)

Thanks for reviving this, I never had the time/motivation to come back to this PR, but it's important.

We should call SECP256K1_CHECKMEM_UNDEFINE (

* - SECP256K1_CHECKMEM_UNDEFINE(p, len):
) in secp256k1_memclear after clearing the memory. Reading cleared memory would clearly be a bug.

@theStack
Copy link
Contributor Author

We should call SECP256K1_CHECKMEM_UNDEFINE (

* - SECP256K1_CHECKMEM_UNDEFINE(p, len):

) in secp256k1_memclear after clearing the memory. Reading cleared memory would clearly be a bug.

Thanks, added that, and rebased on master.

@real-or-random
Copy link
Contributor

@theStack Can you rebase this on top of musig which has introduced a few more code locations that need clearing?

Personally, I'd love to have this in the next release.

@theStack
Copy link
Contributor Author

theStack commented Oct 8, 2024

@theStack Can you rebase this on top of musig which has introduced a few more code locations that need clearing?

Sure, done. With only five lines changed in the musig module, this needed less effort than expected, hope I didn't miss any instances (many of them are handled indirectly via the ..._{fe,scalar}_clear functions though).

Relevant excerpt of the range-diff (uncolored here)
$ git range-diff ac0e41...0b01d2
5:  6fcbae9 ! 15:  02ee811 Use secp256k1_memclear() to clear stack memory instead of memset()
    @@ src/modules/ellswift/main_impl.h: int secp256k1_ellswift_xdh(const secp256k1_con
          secp256k1_scalar_clear(&s);
      
     
    + ## src/modules/musig/session_impl.h ##
    +@@ src/modules/musig/session_impl.h: static void secp256k1_nonce_function_musig(secp256k1_scalar *k, const unsigned c
    +         secp256k1_scalar_set_b32(&k[i], buf, NULL);
    + 
    +         /* Attempt to erase secret data */
    +-        memset(buf, 0, sizeof(buf));
    +-        memset(&sha_tmp, 0, sizeof(sha_tmp));
    ++        secp256k1_memclear(buf, sizeof(buf));
    ++        secp256k1_memclear(&sha_tmp, sizeof(sha_tmp));
    +     }
    +-    memset(rand, 0, sizeof(rand));
    +-    memset(&sha, 0, sizeof(sha));
    ++    secp256k1_memclear(rand, sizeof(rand));
    ++    secp256k1_memclear(&sha, sizeof(sha));
    + }
    + 
    + int secp256k1_musig_nonce_gen_internal(const secp256k1_context* ctx, secp256k1_musig_secnonce *secnonce, secp256k1_musig_pubnonce *pubnonce, const unsigned char *input_nonce, const unsigned char *seckey, const secp256k1_pubkey *pubkey, const unsigned char *msg32, const secp256k1_musig_keyagg_cache *keyagg_cache, const unsigned char *extra_input32) {
    +@@ src/modules/musig/session_impl.h: int secp256k1_musig_nonce_gen_counter(const secp256k1_context* ctx, secp256k1_mu
    +     if (!secp256k1_musig_nonce_gen_internal(ctx, secnonce, pubnonce, buf, seckey, &pubkey, msg32, keyagg_cache, extra_input32)) {
    +         return 0;
    +     }
    +-    memset(seckey, 0, sizeof(seckey));
    ++    secp256k1_memclear(seckey, sizeof(seckey));
    +     return 1;
    + }
    + 
    +

@real-or-random
Copy link
Contributor

This one comes to my mind, too:

secp256k1/src/util.h

Lines 253 to 254 in a88aa93

/* acc may contain secret values. Try to explicitly clear it. */
acc = 0;

@theStack
Copy link
Contributor Author

theStack commented Oct 8, 2024

This one comes to my mind, too:

secp256k1/src/util.h

Lines 253 to 254 in a88aa93

/* acc may contain secret values. Try to explicitly clear it. */
acc = 0;

Ah good catch, missed that (only grepped for memset calls). Tackled now as well.

Copy link
Contributor

@real-or-random real-or-random left a comment

Choose a reason for hiding this comment

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

If you want motivated, you could look at git grep secp256k1_ge_set_gej.*(. @sipa's recent comment in the MuSig PR really caught my attention. When I worked on the previous PR, I really didn't consider the possibility that a gej could leak secret data.


I wonder if it makes sense to have the _finalize functions in the hash module clear the state automatically. And then have "unsafe" funtions like _finalize_keep if the callers knows that data is public or if the caller wants to reuse the midstate. It sounds like a neat idea, but I'm not really convinced because it special-cases the hash module somewhat: we'll need to manually clear everything else including scalars, etc... So we have to be careful with this anyway when writing and reviewing code, and perhaps having yet another safety mechanism that works only for the hash module. (This could perhaps be a nice follow-up if we add the same mechanism to more modules, e.g., ge_set_gej could clear the gejs, unless you use a _keep variant.)

src/util.h Outdated Show resolved Hide resolved
src/secp256k1.c Show resolved Hide resolved
@theStack
Copy link
Contributor Author

If you want motivated, you could look at git grep secp256k1_ge_set_gej.*(. @sipa's recent comment in the MuSig PR really caught my attention. When I worked on the previous PR, I really didn't consider the possibility that a gej could leak secret data.

I found the following functions containing _gej instances resulting from point multiplication (secp256k1_ecmult_{gen,const}) with secret scalars:

Taking care of those could be probably go into an own PR, as its trivial to fix and review and hence has a significantly higher chance to land in the next release than this one? (and having a version where the compiler still might optimize it out seems still much better than not doing it at all)

Interestingly, the ECDSA signing function does clear out the nonce commitment for both the jacobi and affine points (though the latter wouldn't be needed according to #1479 (comment)).

I wonder if it makes sense to have the _finalize functions in the hash module clear the state automatically. And then have "unsafe" funtions like _finalize_keep if the callers knows that data is public or if the caller wants to reuse the midstate. It sounds like a neat idea, but I'm not really convinced because it special-cases the hash module somewhat: we'll need to manually clear everything else including scalars, etc... So we have to be careful with this anyway when writing and reviewing code, and perhaps having yet another safety mechanism that works only for the hash module. (This could perhaps be a nice follow-up if we add the same mechanism to more modules, e.g., ge_set_gej could clear the gejs, unless you use a _keep variant.)

Sounds like a good idea to me for a follow-up.

@real-or-random
Copy link
Contributor

real-or-random commented Oct 10, 2024

Taking care of those could be probably go into an own PR, as its trivial to fix and review and hence has a significantly higher chance to land in the next release than this one? (and having a version where the compiler still might optimize it out seems still much better than not doing it at all)

My guess is that what we do currently is useless on any modern compiler. I admit that I haven't looked at the compiler output, but I'd rather spend the time on resolving the problem.

I don't think that fixing these additional cases here will make it much more difficult to review the PR. And to be honest, while this is a great for defense in depth, we haven't deeply cared about this so far. It's not the end of the world if we need to wait a few more months. So I think it's good to add these cases to this PR here.

@theStack
Copy link
Contributor Author

My guess is that what we do currently is useless on any modern compiler. I admit that I haven't looked at the compiler output, but I'd rather spend the time on resolving the problem.

I don't think that fixing these additional cases here will make it much more difficult to review the PR. And to be honest, while this is a great for defense in depth, we haven't deeply cared about this so far. It's not the end of the world if we need to wait a few more months. So I think it's good to add these cases to this PR here.

Thanks for the feedback! Makes sense, added an extra commit at the end for the gej clearing after point multiplications, it's only four lines of code anyway. Also went over the necessary hash clearing places and made a few small changes (see #1579 (comment)).

@sipa
Copy link
Contributor

sipa commented Oct 21, 2024

utACK c921078. I have not reviewed this for exhaustiveness (as in, are there more places where clearing is useful/necessary), but the code changes look good.

void *(*volatile const volatile_memset)(void *, int, size_t) = memset;
volatile_memset(ptr, 0, len);
#endif
SECP256K1_CHECKMEM_UNDEFINE(ptr, len);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, it seems that in production code, we usually don't call any of the _CHECKMEM_{DEFINE,UNDEFINE} macros. There is one reachable code-path in secp256k1_declassify, but it would only hit if the context was created with the SECP256K1_CONTEXT_DECLASSIFY flag, where the API documentation explicitly says "Do not use". Should that _CHECKMEM_UNDEFINE call here be conditional by a preprocessor #if(def) or something alike, so it only applies to tests and can't slow down (and bloat) release builds?

I noticed that while looking at the disassembly of the .so file and wondering why there was so much extra code after the memory clearing, until I realized this must be valgrind's VALGRIND_MAKE_MEM_UNDEFINED. I'm building now explicitly with -DSECP256K1_VALGRIND=OFF -DSECP256K1_BUILD_CTIME_TESTS=OFF to avoid that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, great point! The reason why secp256k1_declassify uses a run-time flag (instead of compile-time flag) is that we want to run the constant-time tests on the real binary.

I don't know what the performance impact of these additional instructions is, but I doubt that having a compile-time flag is a concern in this case. That means that we could just wrap the SECP256K1_CHECKMEM_UNDEFINE in an #ifdef VERIFY. @sipa What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the reasoning is that we want the release binaries to be as close as possible to what we actually test in the ctime test, but disable the effect at runtime using SECP256K1_CONTEXT_DECLASSIFY to avoid a performance impact.

It wouldn't surprise me that that is overkill; the conditional to determine whether or not to declassify operations at runtime may have a higher cost than actually executing the nop instructions that declassification actually correspond to when not running instrumented by valgrind.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, sorry, I think we're talking past each other. My suggestion, for which I'd like to have your opinion on, is to wrap SECP256K1_CHECKMEM_UNDEFINE in an #ifdef VERIFY block here in this particular case of clearing memory, i.e., inside secp256k1_memclear (and not in secp256k1_declassify. Do you think that's a reasonable thing to do?


But regarding what you said:

I think the reasoning is that we want the release binaries to be as close as possible to what we actually test in the ctime test, but disable the effect at runtime using SECP256K1_CONTEXT_DECLASSIFY to avoid a performance impact.

Yes, I agree. Perhaps an additional reason is to avoid any shenanigans that the valgrind syscalls may have. They should be noops, but it's certainly a bit safer not to run them at all.

It wouldn't surprise me that that is overkill; the conditional to determine whether or not to declassify operations at runtime may have a higher cost than actually executing the nop instructions that declassification actually correspond to when not running instrumented by valgrind.

I can imagine that the overhead of the noops is negligible, but at least checking the conditional should be negligible as well because we use EXCEPT to help the compiler predict the branch:

secp256k1/src/secp256k1.c

Lines 236 to 238 in 68b5520

static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, const void *p, size_t len) {
if (EXPECT(ctx->declassify, 0)) SECP256K1_CHECKMEM_DEFINE(p, len);
}

Copy link
Contributor Author

@theStack theStack Oct 22, 2024

Choose a reason for hiding this comment

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

What bothered me in general (independent on how lightweight the code generated by the valgrind macros might be) is that when users build with the default CMake settings, they could currently end up with different release binaries, depending on whether valgrind is installed or not. Unrelated to this PR, but maybe the SECP256K1_VALGRIND build setting should only be auto-detected in the "dev-mode" preset, and OFF by default? (I only looked at the CMake build, don't know if the valgrind setting is also auto-detected on autotools).

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. That's a valid point, which has been brought up before, see #813 (comment). (This thread is also very helpful to understand why we have run-time flag etc.) The conclusion was that the benefits outweigh the drawback that the build outputs depends on the availability of valgrind, but yeah, there's no perfect solution here. And yes, it's also auto-detected in autotools.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, so the idea is to have

...
#ifndef VERIFY
     SECP256K1_CHECKMEM_UNDEFINE(ptr, len);
#endif
}

in secp256k1_memclear()?

That would mean tests_noverify wouldn't detect violations of use-after-clear, nor would applications linking against the production library when running in valgrind. The upside would be that the production library definitely does not have its behavior affected by being compiled with valgrind present.

Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed.

That would mean tests_noverify wouldn't detect violations of use-after-clear

Fwiw, I had this downside in mind, and I don't think it's a big deal because we still have the normal tests.

nor would applications linking against the production library when running in valgrind.

Okay, I had not considered this one... I still think that's acceptable, but yeah, it's a good point...

Copy link
Contributor

Choose a reason for hiding this comment

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

ACK. I guess it makes sense that if the primary goal is making sure no runtime impact for production code, this should be guarded by a compile-time check, as the runtime check isn't available here (being just for ctime testing).

@theStack
Copy link
Contributor Author

theStack commented Oct 22, 2024

For a first check on whether this PR fulfills its promise, I looked at the generated assembler code of secp256k1_ec_seckey_verify, as this function is particularly short and should clear out the secret scalar at the end. The following commands were used to generate the disassembled output:

$ cmake --build build && objdump --no-addresses --no-show-raw-insn --disassemble=secp256k1_ec_seckey_verify ./build/lib/libsecp256k1.so > seckey_verify_disasm.txt
compiler output diff of `secp256k1_ec_seckey_verify` (master vs. PR branch)

running on Debian 12 stable (Bookworm) and gcc 12.2.0 on a x86-64 machine

--- master.txt  2024-10-22 16:09:38.985554751 +0200
+++ pr1579.txt  2024-10-22 16:12:37.324112394 +0200
@@ -11,31 +11,38 @@
 Disassembly of section .text:
 
 <secp256k1_ec_seckey_verify>:
-       sub    $0x38,%rsp
+       push   %rbx
+       sub    $0x30,%rsp
        test   %rsi,%rsi
-       je     <secp256k1_ec_seckey_verify+0x48>
+       je     <secp256k1_ec_seckey_verify+0x58>
+       lea    0x10(%rsp),%rbx
        lea    0xc(%rsp),%rdx
-       lea    0x10(%rsp),%rdi
+       pxor   %xmm0,%xmm0
+       mov    %rbx,%rdi
        call   <secp256k1_scalar_set_b32>
        mov    0xc(%rsp),%ecx
        mov    0x10(%rsp),%rax
        or     0x18(%rsp),%rax
        or     0x20(%rsp),%rax
+       movaps %xmm0,0x10(%rsp)
        or     0x28(%rsp),%rax
+       movaps %xmm0,0x20(%rsp)
        setne  %dl
        xor    %eax,%eax
        test   %ecx,%ecx
        sete   %al
-       add    $0x38,%rsp
        and    %edx,%eax
+       add    $0x30,%rsp
+       pop    %rbx
        ret
        nopl   0x0(%rax)
        mov    %rdi,%rax
        mov    0xb0(%rdi),%rsi
-       lea    0x63b9(%rip),%rdi        # <_fini+0x1196>
+       lea    0x6199(%rip),%rdi        # <_fini+0xf46>
        call   *0xa8(%rax)
+       add    $0x30,%rsp
        xor    %eax,%eax
-       add    $0x38,%rsp
+       pop    %rbx
        ret
There is a little noise in the diff due to different register allocations and jump offsets, but in the middle one can see quite clearly that the memory clearing happens using the 16-byte SSE register xmm0: it is earlier zeroed with a `pxor` instruction and then written with `movaps` instruction twice to memory locations `0x10(%rsp)` and `0x20(%rsp)`, which correspond to the 32-byte `secp256k1_scalar` instance (interestingly, the clearing is interleaved with the `mov`/`or` instructions of the `_scalar_is_zero` check). So this seems to work as expected. Might be interesting to also try it with a variety of other compiler versions (especially newer ones) and different architectures, like arm64.

Copy link
Contributor

@real-or-random real-or-random left a comment

Choose a reason for hiding this comment

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

ACK mod my review comments

src/util.h Outdated Show resolved Hide resolved
src/util.h Show resolved Hide resolved
src/util.h Show resolved Hide resolved
This code is not supposed to handle secret data.
@theStack
Copy link
Contributor Author

Fixed #1579 (comment) and rebased on master for easier comparison of branches (since #1553, the output location of the .so file changed from ./build/src to ./build/lib; not being aware of that, I was unintentionally comparing the library file generated from master with itself for quite some time, wondering why the PR doesn't change anything 🤦‍♂️ ).

@theStack
Copy link
Contributor Author

theStack commented Oct 25, 2024

Regarding the open question of whether or not to include the _CHECKMEM_UNDEFINE macro in secp256k1_memclear in non-verify binaries or not, I compared the valgrind-builds on the pr branch with the macro commented out vs. with the macro included. This is what the assembler diff looks like (created for the same function and using the same methodology as in #1579 (comment)):

disassemble of secp256k1_ec_seckey_verify, without and with the SECP256K1_CHECKMEM_UNDEFINE in a valgrind build
--- pr1579_without_checkmem_undefine.txt	2024-10-25 17:34:50.639344958 +0200
+++ pr1579_with_checkmem_undefine.txt	2024-10-25 17:33:35.632626088 +0200
@@ -11,38 +11,59 @@
 Disassembly of section .text:
 
 <secp256k1_ec_seckey_verify>:
+	push   %rbp
 	push   %rbx
-	sub    $0x30,%rsp
+	sub    $0x68,%rsp
 	test   %rsi,%rsi
-	je     <secp256k1_ec_seckey_verify+0x58>
+	je     <secp256k1_ec_seckey_verify+0xb8>
+	lea    0x30(%rsp),%rbp
 	lea    0x10(%rsp),%rbx
-	lea    0xc(%rsp),%rdx
 	pxor   %xmm0,%xmm0
+	mov    %rbp,%rdx
 	mov    %rbx,%rdi
 	call   <secp256k1_scalar_set_b32>
-	mov    0xc(%rsp),%ecx
-	mov    0x10(%rsp),%rax
-	or     0x18(%rsp),%rax
-	or     0x20(%rsp),%rax
+	mov    0x10(%rsp),%rdx
+	or     0x18(%rsp),%rdx
 	movaps %xmm0,0x10(%rsp)
-	or     0x28(%rsp),%rax
+	or     0x20(%rsp),%rdx
+	or     0x28(%rsp),%rdx
 	movaps %xmm0,0x20(%rsp)
-	setne  %dl
-	xor    %eax,%eax
-	test   %ecx,%ecx
-	sete   %al
-	and    %edx,%eax
-	add    $0x30,%rsp
+	mov    0x30(%rsp),%edx
+	setne  %al
+	xor    %ecx,%ecx
+	test   %edx,%edx
+	sete   %cl
+	and    %eax,%ecx
+	movq   $0x4d430001,0x30(%rsp)
+	xor    %edx,%edx
+	mov    %rbp,%rax
+	mov    %rbx,0x38(%rsp)
+	movq   $0x20,0x40(%rsp)
+	movq   $0x0,0x48(%rsp)
+	movq   $0x0,0x50(%rsp)
+	movq   $0x0,0x58(%rsp)
+	rol    $0x3,%rdi
+	rol    $0xd,%rdi
+	rol    $0x3d,%rdi
+	rol    $0x33,%rdi
+	xchg   %rbx,%rbx
+	mov    %rdx,0x8(%rsp)
+	mov    0x8(%rsp),%rax
+	add    $0x68,%rsp
+	mov    %ecx,%eax
 	pop    %rbx
+	pop    %rbp
 	ret
-	nopl   0x0(%rax)
+	nopl   0x0(%rax,%rax,1)
 	mov    %rdi,%rax
 	mov    0xb0(%rdi),%rsi
-	lea    0x5d09(%rip),%rdi        # <_fini+0x766>
+	lea    0x7b99(%rip),%rdi        # <_fini+0x14e6>
 	call   *0xa8(%rax)
-	add    $0x30,%rsp
-	xor    %eax,%eax
+	add    $0x68,%rsp
+	xor    %ecx,%ecx
+	mov    %ecx,%eax
 	pop    %rbx
+	pop    %rbp
 	ret
 
 Disassembly of section .fini:

Seems like at least in this function, it causes at least 15 extra (no-op like) instructions to be emitted. Considering that previously, these valgrind placeholders were not reachable (unless explicitly specified in the context object) and these extra instructions are added at multiple places spread all over the code, it might be better to not include them in the release binaries? OTOH, "ctime-tests should run with the same binary as in the release" then doesn't hold anymore. So really not sure what's better 🤔

@real-or-random
Copy link
Contributor

OTOH, "ctime-tests should run with the same binary as in the release" then doesn't hold anymore.

I don't think so. If we wrap the new _CHECKMEM_UNDEFINE within _memclear in #ifdef VERIFY, I don't see how this affects the ctimetests at all. We'd still include the _CHECKMEM_UNDEFINE in declassify in the release builds. And the ctimetests executable will still link against the (release) binary of the library. (Note that the ctimetests test executable uses the public API, it's really just an application that consumes the library by linking against it, as opposed to the other test binaries, which essentially #include all the library code.)

We rely on memset() and an __asm__ memory barrier where it's available or
on SecureZeroMemory() on Windows. The fallback implementation uses a
volatile function pointer to memset which the compiler is not clever
enough to optimize.
real-or-random and others added 7 commits October 25, 2024 18:44
There are two uses of the secp256k1_fe_clear() function that are now separated
into these two functions in order to reflect the intent:

1) initializing the memory prior to being used -> converted to fe_set_int( . , 0 )
2) zeroing the memory after being used such that no sensitive data remains. ->
    remains as fe_clear()

In the latter case, 'magnitude' and 'normalized' need to be overwritten when
VERIFY is enabled.

Co-Authored-By: isle2983 <isle2983@yahoo.com>
Co-Authored-By: isle2983 <isle2983@yahoo.com>
Co-Authored-By: Pieter Wuille <pieter.wuille@gmail.com>
All of the invocations of secp256k1_memclear() operate on stack
memory and happen after the function is done with the memory object.
This commit replaces existing memset() invocations and also adds
secp256k1_memclear() to code locations where clearing was missing;
there is no guarantee that this commit covers all code locations
where clearing is necessary.

Co-Authored-By: isle2983 <isle2983@yahoo.com>
This gives the caller more control about whether the state should
be cleaned (= should be considered secret). Moreover, it gives the
caller the possibility to clean a hash struct without finalizing it.
Quoting sipa (see bitcoin-core#1479 (comment)):
"When performing an EC multiplication A = aG for secret a, the resulting
 _affine_ coordinates of A are presumed to not leak information about a (ECDLP),
  but the same is not necessarily true for the Jacobian coordinates that come
  out of our multiplication algorithm."

For the ECDH point multiplication result, the result in Jacobi coordinates should be
cleared not only to avoid leaking the scalar, but even more so as it's a representation
of the resulting shared secret.
@theStack
Copy link
Contributor Author

OTOH, "ctime-tests should run with the same binary as in the release" then doesn't hold anymore.

I don't think so. If we wrap the new _CHECKMEM_UNDEFINE within _memclear in #ifdef VERIFY, I don't see how this affects the ctimetests at all. We'd still include the _CHECKMEM_UNDEFINE in declassify in the release builds. And the ctimetests executable will still link against the (release) binary of the library. (Note that the ctimetests test executable uses the public API, it's really just an application that consumes the library by linking against it, as opposed to the other test binaries, which essentially #include all the library code.)

Oh right, for some reason I assumed that the ctimetests binary is also compiled with VERIFY defined, which is of course nonsensical. Added the "#ifdef VERIFY" block around _CHECKMEM_UNDEFINE now, as it looks we have consensus on doing that (#1579 (comment)).

Copy link
Contributor

@real-or-random real-or-random left a comment

Choose a reason for hiding this comment

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

ACK 765ef53

@sipa
Copy link
Contributor

sipa commented Nov 4, 2024

reACK 765ef53

@real-or-random real-or-random merged commit b161bff into bitcoin-core:master Nov 4, 2024
116 checks passed
@theStack theStack deleted the revival_of_pr636_cleanse branch November 4, 2024 15:53
achow101 added a commit to achow101/bitcoin that referenced this pull request Nov 4, 2024
0cdc758a563 Merge bitcoin-core/secp256k1#1631: release: prepare for 0.6.0
39d5dfd542a release: prepare for 0.6.0
df2eceb2790 build: add ellswift.md and musig.md to release tarball
a306bb7e903 tools: fix check-abi.sh after cmake out locations were changed
145868a84d2 Do not export `secp256k1_musig_nonce_gen_internal`
b161bffb8bf Merge bitcoin-core/secp256k1#1579: Clear sensitive memory without getting optimized out (revival of bitcoin#636)
a38d879a1a6 Merge bitcoin-core/secp256k1#1628: Name public API structs
7d48f5ed02e Merge bitcoin-core/secp256k1#1581: test, ci: Lower default iteration count to 16
694342fdb71 Name public API structs
0f73caf7c62 test, ci: Lower default iteration count to 16
9a8db52f4e9 Merge bitcoin-core/secp256k1#1582: cmake, test: Add `secp256k1_` prefix to test names
765ef53335a Clear _gej instances after point multiplication to avoid potential leaks
349e6ab916b Introduce separate _clear functions for hash module
99cc9fd6d01 Don't rely on memset to set signed integers to 0
97c57f42ba8 Implement various _clear() functions with secp256k1_memclear()
9bb368d1466 Use secp256k1_memclear() to clear stack memory instead of memset()
e3497bbf001 Separate between clearing memory and setting to zero in tests
d79a6ccd43a Separate secp256k1_fe_set_int( . , 0 ) from secp256k1_fe_clear()
1c081262227 Add secp256k1_memclear() for clearing secret data
1464f15c812 Merge bitcoin-core/secp256k1#1625: util: Remove unused (u)int64_t formatting macros
980c08df80a util: Remove unused (u)int64_t formatting macros
9b7c59cbb90 Merge bitcoin-core/secp256k1#1624: ci: Update macOS image
096e3e23f63 ci: Update macOS image
e7d384488e8 Don't clear secrets in pippenger implementation
68b55209f1b Merge bitcoin-core/secp256k1#1619: musig: ctimetests: fix _declassify range for generated nonce points
f0868a9b3d8 Merge bitcoin-core/secp256k1#1595: build: 45839th attempt to fix symbol visibility on Windows
1fae76f50c0 Merge bitcoin-core/secp256k1#1620: Remove unused scratch space from API
8be3839fb2e Remove unused scratch space from API
57eda3ba300 musig: ctimetests: fix _declassify range for generated nonce points
87384f5c0f2 cmake, test: Add `secp256k1_` prefix to test names
e59158b6eb7 Merge bitcoin-core/secp256k1#1553: cmake: Set top-level target output locations
18f9b967c25 Merge bitcoin-core/secp256k1#1616: examples: do not retry generating seckey randomness in musig
5bab8f6d3c4 examples: make key generation doc consistent
e8908221a45 examples: do not retry generating seckey randomness in musig
70b6be1834e extrakeys: improve doc of keypair_create (don't suggest retry)
01b5893389e Merge bitcoin-core/secp256k1#1599: bitcoin#1570 improve examples: remove key generation loop
cd4f84f3ba8 Improve examples/documentation: remove key generation loops
a88aa935063 Merge bitcoin-core/secp256k1#1603: f can never equal -m
3660fe5e2a9 Merge bitcoin-core/secp256k1#1479: Add module "musig" that implements MuSig2 multi-signatures (BIP 327)
168c92011f5 build: allow enabling the musig module in cmake
f411841a46b Add module "musig" that implements MuSig2 multi-signatures (BIP 327)
0be79660f38 util: add constant-time is_zero_array function
c8fbdb1b972 group: add ge_to_bytes_ext and ge_from_bytes_ext
ef7ff03407f f can never equal -m
c232486d84e Revert "cmake: Set `ENVIRONMENT` property for examples on Windows"
26e4a7c2146 cmake: Set top-level target output locations
4c57c7a5a95 Merge bitcoin-core/secp256k1#1554: cmake: Clean up testing code
447334cb06d include: Avoid visibility("default") on Windows
472faaa8ee6 Merge bitcoin-core/secp256k1#1604: doc: fix typos in `secp256k1_ecdsa_{recoverable_,}signature` API description
292310fbb24 doc: fix typos in `secp256k1_ecdsa_{recoverable_,}signature` API description
85e224dd97f group: add ge_to_bytes and ge_from_bytes
7c987ec89e6 cmake: Call `enable_testing()` unconditionally
6aa576515ef cmake: Delete `CTest` module

git-subtree-dir: src/secp256k1
git-subtree-split: 0cdc758a56360bf58a851fe91085a327ec97685a
vmta added a commit to umkoin/umkoin that referenced this pull request Nov 6, 2024
8deef00b3 Merge bitcoin-core/secp256k1#1634: Fix some misspellings
39705450e Fix some misspellings
ec329c250 Merge bitcoin-core/secp256k1#1633: release cleanup: bump version after 0.6.0
c97059f59 release cleanup: bump version after 0.6.0
0cdc758a5 Merge bitcoin-core/secp256k1#1631: release: prepare for 0.6.0
39d5dfd54 release: prepare for 0.6.0
df2eceb27 build: add ellswift.md and musig.md to release tarball
a306bb7e9 tools: fix check-abi.sh after cmake out locations were changed
145868a84 Do not export `secp256k1_musig_nonce_gen_internal`
b161bffb8 Merge bitcoin-core/secp256k1#1579: Clear sensitive memory without getting optimized out (revival of #636)
a38d879a1 Merge bitcoin-core/secp256k1#1628: Name public API structs
7d48f5ed0 Merge bitcoin-core/secp256k1#1581: test, ci: Lower default iteration count to 16
694342fdb Name public API structs
0f73caf7c test, ci: Lower default iteration count to 16
9a8db52f4 Merge bitcoin-core/secp256k1#1582: cmake, test: Add `secp256k1_` prefix to test names
765ef5333 Clear _gej instances after point multiplication to avoid potential leaks
349e6ab91 Introduce separate _clear functions for hash module
99cc9fd6d Don't rely on memset to set signed integers to 0
97c57f42b Implement various _clear() functions with secp256k1_memclear()
9bb368d14 Use secp256k1_memclear() to clear stack memory instead of memset()
e3497bbf0 Separate between clearing memory and setting to zero in tests
d79a6ccd4 Separate secp256k1_fe_set_int( . , 0 ) from secp256k1_fe_clear()
1c0812622 Add secp256k1_memclear() for clearing secret data
e7d384488 Don't clear secrets in pippenger implementation
87384f5c0 cmake, test: Add `secp256k1_` prefix to test names

git-subtree-dir: src/secp256k1
git-subtree-split: 8deef00b33ca81202aca80fe0bcd9730f084fbd2
vmta added a commit to umkoin/umkoin that referenced this pull request Nov 6, 2024
8deef00b3 Merge bitcoin-core/secp256k1#1634: Fix some misspellings
39705450e Fix some misspellings
ec329c250 Merge bitcoin-core/secp256k1#1633: release cleanup: bump version after 0.6.0
c97059f59 release cleanup: bump version after 0.6.0
0cdc758a5 Merge bitcoin-core/secp256k1#1631: release: prepare for 0.6.0
39d5dfd54 release: prepare for 0.6.0
df2eceb27 build: add ellswift.md and musig.md to release tarball
a306bb7e9 tools: fix check-abi.sh after cmake out locations were changed
145868a84 Do not export `secp256k1_musig_nonce_gen_internal`
b161bffb8 Merge bitcoin-core/secp256k1#1579: Clear sensitive memory without getting optimized out (revival of #636)
a38d879a1 Merge bitcoin-core/secp256k1#1628: Name public API structs
7d48f5ed0 Merge bitcoin-core/secp256k1#1581: test, ci: Lower default iteration count to 16
694342fdb Name public API structs
0f73caf7c test, ci: Lower default iteration count to 16
9a8db52f4 Merge bitcoin-core/secp256k1#1582: cmake, test: Add `secp256k1_` prefix to test names
765ef5333 Clear _gej instances after point multiplication to avoid potential leaks
349e6ab91 Introduce separate _clear functions for hash module
99cc9fd6d Don't rely on memset to set signed integers to 0
97c57f42b Implement various _clear() functions with secp256k1_memclear()
9bb368d14 Use secp256k1_memclear() to clear stack memory instead of memset()
e3497bbf0 Separate between clearing memory and setting to zero in tests
d79a6ccd4 Separate secp256k1_fe_set_int( . , 0 ) from secp256k1_fe_clear()
1c0812622 Add secp256k1_memclear() for clearing secret data
e7d384488 Don't clear secrets in pippenger implementation
87384f5c0 cmake, test: Add `secp256k1_` prefix to test names

git-subtree-dir: src/secp256k1
git-subtree-split: 8deef00b33ca81202aca80fe0bcd9730f084fbd2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Memory zeroization improvements
3 participants