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

secp256k1: Harden const time field normalization. #2258

Merged
merged 1 commit into from
Jul 14, 2020

Conversation

davecgh
Copy link
Member

@davecgh davecgh commented Jul 12, 2020

This updates the field normalization code to better secure against the possibility of non-constant time operations due to branch prediction and adds several tests to ensure the new logic is sound.

The following benchmark results show that this implementation is within the margin of error for it to not be statistically relevant and thus has no performance impact.

name             old time/op   new time/op   delta
----------------------------------------------------------------------
FieldNormalize   22.1ns ± 1%   22.1ns ± 1%   ~     (p=0.873 n=5+5)

This is primarily based on the work of @bmperrea in btcsuite/btcd#1084 based on questions originally raised by @stevenroose. However, this differs in that it uses an ever so slightly faster implementation by reversing the comparison logic to reduce the number of primitives needed, uses internal functions for constant time comparison, and adds more complete tests for all of the possible combinations.

@davecgh davecgh added this to the 1.6.0 milestone Jul 12, 2020
@davecgh davecgh force-pushed the secp256k1_const_field_normalize branch 2 times, most recently from ce6c9b3 to 264cbf8 Compare July 12, 2020 02:28
@davecgh davecgh force-pushed the secp256k1_const_field_normalize branch from 264cbf8 to 77ac77d Compare July 12, 2020 19:04
Copy link
Member

@rstaudt2 rstaudt2 left a comment

Choose a reason for hiding this comment

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

This looks good to me, nicely done. I don't see any issues with the logic and also compared the benchmark to what is currently in master.

I was also looking into the possibility of a dynamic approach to detecting a non-constant time function in go and didn't find anything that could quickly be applied, but the approach outlined in this paper is interesting: https://courses.csail.mit.edu/6.857/2015/files/dove-vasiliev.pdf

@davecgh davecgh force-pushed the secp256k1_const_field_normalize branch 3 times, most recently from abd8a97 to a886dcd Compare July 13, 2020 00:14
@davecgh
Copy link
Member Author

davecgh commented Jul 13, 2020

I updated the PR and commit descriptions to include the benchmark statistics across 5 runs of both the previous and new code.

@davecgh
Copy link
Member Author

davecgh commented Jul 13, 2020

@rstaudt2 Thanks for the review. It is indeed challenging to prove constant time in general. The way I personally go about it is by looking at the assembly output to identify any DDTVs (data-dependent timing variations), but a more dynamic approach would certainly be useful.

Copy link
Member

@JoeGruffins JoeGruffins left a comment

Choose a reason for hiding this comment

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

Wow. That btcd pr sitting there since 2017...

For me, it looks like the bench has slowed from master. Are my commands correct?

$ git checkout master
Switched to branch 'master'
$ go test -bench=BenchmarkFieldNormalize -benchtime 20s > old.txt
$ git checkout 2258
Switched to branch '2258'
$ go test -bench=BenchmarkFieldNormalize -benchtime 20s > new.txt
$ benchstat old.txt new.txt 
name              old time/op  new time/op  delta
FieldNormalize-8  18.5ns ± 0%  19.2ns ± 0%   ~     (p=1.000 n=1+1)

count 5 and not setting -benchtime

name              old time/op  new time/op  delta
FieldNormalize-8  19.3ns ± 1%  20.1ns ± 1%  +4.35%  (p=0.008 n=5+5)

@davecgh
Copy link
Member Author

davecgh commented Jul 14, 2020

That ~ indicates that it is statistically insignificant. However, I ran the benchmark 5 times on each appending the results. That's why you see n=5+5 in my results. Keep in mind that we're dealing with nanoseconds here, so you have to make sure nothing else is happening on your machine when you benchmark these. That means closing everything except the shell you're benchmarking from. I have a VM that I use specifically for this purpose.

@davecgh
Copy link
Member Author

davecgh commented Jul 14, 2020

For reference, here is the relevant assembly from both:

You can see the old code is doing comparisons via CMPL and TESTL whereas the new code is not.

Old:

CMPL	CX, $4194303
SETEQ	DL
MOVL	R8, R14
ANDL	DI, R8
ANDL	R9, R8
ANDL	R10, R8
ANDL	R11, R8
ANDL	R12, R8
ANDL	R13, R8
ANDL	$67108863, R8
MOVBLZX	DL, DX
ANDL	$1, DX
CMPL	R8, $67108863
MOVL	$0, R8
CMOVLEQ	DX, R8
ANDL	$67108863, SI
ANDL	$67108863, BX
LEAL	977(SI), DX
SHRL	$26, DX
LEAL	(DX)(BX*1), DX
LEAL	64(DX), DX
ANDL	$1, R8
CMPL	DX, $67108863
MOVL	$0, DX
CMOVLHI	R8, DX
MOVL	CX, R8
SHRL	$22, CX
MOVL	DX, R15
ORL	$1, DX
TESTL	CX, CX

New:

XCHGL	AX, AX
MOVL	R13, DX
ANDL	R12, R13
ANDL	R11, R13
ANDL	R10, R13
ANDL	R9, R13
ANDL	R8, R13
ANDL	DI, R13
ANDL	$67108863, R13
MOVL	CX, R14
XORL	$4194303, CX
DECQ	CX
SHRQ	$63, CX
XORL	$67108863, R13
DECQ	R13
SHRQ	$63, R13
ANDL	R13, CX
LEAL	977(SI), R13
SHRL	$26, R13
LEAL	(R13)(BX*1), R13
LEAL	64(R13), R13
ADDQ	$-67108863, R13
NEGQ	R13
SHRQ	$63, R13
ANDL	CX, R13
MOVL	R14, CX
SHRL	$22, R14
ORL	R14, R13

This updates the field normalization code to better secure against the
possibility of non-constant time operations due to branch prediction and
adds several tests to ensure the new logic is sound.

The following benchmark results show that this implementation is within
the margin of error for it to not be statistically relevant and thus has
no performance impact.

name             old time/op   new time/op   delta
----------------------------------------------------------------------
FieldNormalize   22.1ns ± 1%   22.1ns ± 1%   ~     (p=0.873 n=5+5)
@davecgh davecgh force-pushed the secp256k1_const_field_normalize branch from a886dcd to 5b1b776 Compare July 14, 2020 19:16
@davecgh davecgh merged commit 5b1b776 into decred:master Jul 14, 2020
@davecgh davecgh deleted the secp256k1_const_field_normalize branch July 14, 2020 19:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants