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

crypto/elliptic: different ecdsa.Verify result between p256 amd64 and generic implementations with a zero hash #20215

Closed
tzneal opened this issue May 3, 2017 · 13 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. Security
Milestone

Comments

@tzneal
Copy link
Member

tzneal commented May 3, 2017

What version of Go are you using (go version)?

tip

What operating system and processor architecture are you using (go env)?

GOARCH=386 and GOARCH=amd64

What did you do?

Code pulled from golang-nuts post https://groups.google.com/forum/#!topic/golang-nuts/M7tXCf8a4pk

$ GOARCH=386 go build -o cr32 && ./cr32
2017/05/02 21:10:00 Verification succeded
$ go build -o cr64 && ./cr64
2017/05/02 21:10:00 Failed

What did you expect to see?

I assume the verification should have failed in both cases.

What did you see instead?

Verification passed for the 32 bit binary.

Cause

The difference is in the path taken in ecdsa.Verify:

	var x, y *big.Int
	if opt, ok := c.(combinedMult); ok {
		x, y = opt.CombinedMult(pub.X, pub.Y, u1.Bytes(), u2.Bytes())
	} else {
		x1, y1 := c.ScalarBaseMult(u1.Bytes())
		x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
		x, y = c.Add(x1, y1, x2, y2)
	}

The S390 and AMD64 versions of CombinedMult appear to always set the Z value to 1 regardless of X and Y.

(*curveParams).Add calls zForAffine which returns a Z value of 0 if X or Y are zero.

Modifying zForAffine to match the behavior of the optimized CombinedMults (Z=1 in all cases) causes TestInfinity to fail, but otherwise the generic and optimized CombinedMults give the same output for this case.

@tzneal tzneal changed the title crypto/elliptic: different ecsa.Verify result between p256 amd64 and generic implementations with a null hash crypto/elliptic: different ecdsa.Verify result between p256 amd64 and generic implementations with a null hash May 3, 2017
@bradfitz bradfitz added NeedsFix The path to resolution is known, but the work has not been done. Security labels May 3, 2017
@bradfitz bradfitz added this to the Go1.9 milestone May 3, 2017
@bradfitz
Copy link
Contributor

bradfitz commented May 3, 2017

Saving others a click, code is:
https://gist.github.com/ivoras/32b2abd16b5984fa43c006486bfb7e2c

@bradfitz
Copy link
Contributor

bradfitz commented May 3, 2017

Verified the 386/amd64 difference is the same in Go 1.7, Go 1.8, and tip.

@gopherbot
Copy link

CL https://golang.org/cl/42590 mentions this issue.

@agl agl changed the title crypto/elliptic: different ecdsa.Verify result between p256 amd64 and generic implementations with a null hash crypto/elliptic: different ecdsa.Verify result between p256 amd64 and generic implementations with a zero hash May 4, 2017
@agl
Copy link
Contributor

agl commented May 4, 2017

(I believe the verification should have passed in both cases, rather than failed. I.e. the referenced test case appears to be a valid signature of a zero hash. That means that we're rejecting a valid signature rather than accepting an invalid signature, which is less of a problem at least.)

@agl
Copy link
Contributor

agl commented May 4, 2017

(For my own reference in the future: there's an incomplete fix in https://go-review.googlesource.com/c/42611, but it'll need assembly changes and I'm not going to attempt that today; I'll only mess it up.)

@gopherbot
Copy link

CL https://golang.org/cl/42611 mentions this issue.

@bradfitz
Copy link
Contributor

@agl, do you want this in Go 1.8.2? If so, can we wrap up the review? If not, we can roll Go 1.8.2 already with just #20040 fix.

@bradfitz
Copy link
Contributor

(I believe the verification should have passed in both cases, rather than failed. I.e. the referenced test case appears to be a valid signature of a zero hash. That means that we're rejecting a valid signature rather than accepting an invalid signature, which is less of a problem at least.)

Per that comment, I'm going to mark this as Go 1.9 material and not Go 1.8.2.

@bradfitz bradfitz modified the milestones: Go1.9, Go1.8.2 May 19, 2017
@bradfitz
Copy link
Contributor

@agl, do you want this (your https://go-review.googlesource.com/c/42611/) in Go 1.9 or should we bump it to Go 1.10?

@AnomalRoil
Copy link

AnomalRoil commented Aug 15, 2017

The question here should also be "what should we do if we have a zero hash in ECDSA, knowing that this may partly breaks ECDSA"...
If you have a null hash then the verification process goes as usual with correctly generated signature, excepted that you can trick it into validating your message simply by providing the signature (r,s)=(x,x) where (x,y) are the coordinates of the ECDSA public key !
Also, note that all dependency upon the secret material is removed when the hash value is congruent to 0 mod n.

A crafted signature would then validate since upon verification you compute:

  • e = H(m) = 0.
  • Let z be the Ln (the bit length of the group order n) leftmost bits of e, so it's zero.
  • w = s-1 = x-1 (mod n) by my choice of s.
  • u1 = zw ≡ 0 (mod n) and u2 = rw = xw ≡ 1 (mod n).
  • the curve point computed in the verification becomes:
    (x1, y1) = u1⋅ G + u2 ⋅ Q = 0 ⋅ G + 1 ⋅ Q = Q = (x, y)
  • The signature is valid if x1 ≡ r (mod n), invalid otherwise, but this is the case by choice of the signature's value r = x.

Now, we should ask ourselves what we want to do when confronted to such a zero hash, or such a strange signature... So the zero hash does not permit us to ensure the authenticity of the signature since such forgeries are possible (note that there might be other less trivial forgeries).

So far, I've done a few tests on the topic to see how other are handling it and have found that:

  • OpenSSL will gladly validate such signatures, both valid and crafted.
  • mbedTLS answers with an error "MBEDTLS_ERR_ECP_INVALID_KEY" on zero values upon verification, but not upon signature as it generates valid signatures (which are validated on the Playground or by OpenSSL).
  • CryptoPP will reject the signatures it generated using a zero hash, but while it accepts the crafted signatures or signatures made using Go, its signatures are rejected by other implementations because they are not valid.
  • Go crypto currently rejects such signature on my computer (amd64) but accept them on the playground as proved by this playground example I just made: https://play.golang.org/p/WTic53IPm6 whose code returns twice false on my computer, but true on the playground. (which is exactly why this issue exists in the first place)

I must say that I'm not really sure what would be the best way around this issue. I believe that we should reject the signatures as soon as the values u1 or u2 are zero, or throw an error maybe. (This is fine since secret material is not involved in the verification process and signing the zero hash value does not leak secret material as long a the k value is secret and well randomly generated.)

I don't know what is your opinion on that topic @agl ?

@gopherbot
Copy link

Change https://golang.org/cl/62630 mentions this issue: crypto/elliptic: drop s390x assembly.

@gopherbot
Copy link

Change https://golang.org/cl/62292 mentions this issue: crypto/elliptic: temporarily disable s390x assembly

gopherbot pushed a commit that referenced this issue Sep 11, 2017
This disables the s390x assembly. It will be re-enabled when #20215
is resolved on s390x.

Change-Id: I789eca2dd478004956107359fae98ed012f04abb
Reviewed-on: https://go-review.googlesource.com/62292
Run-TryBot: Michael Munday <mike.munday@ibm.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
@gopherbot
Copy link

Change https://golang.org/cl/64290 mentions this issue: crypto/elliptic: fix incomplete addition used in CombinedMult on s390x

gopherbot pushed a commit that referenced this issue Oct 5, 2017
This applies the amd64-specific changes from CL 42611 to the s390x P256
implementation. The s390x implementation was disabled in CL 62292 and
this CL re-enables it.

Adam Langley's commit message from CL 42611:

The optimised P-256 includes a CombinedMult function, which doesn't do
dual-scalar multiplication, but does avoid an affine conversion for
ECDSA verification.

However, it currently uses an assembly point addition function that
doesn't handle exceptional cases.

Fixes #20215.

Change-Id: I2f6b532f495e85b8903475b4f64cc32a3b2f6769
Reviewed-on: https://go-review.googlesource.com/64290
Run-TryBot: Michael Munday <mike.munday@ibm.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
@golang golang locked and limited conversation to collaborators Sep 18, 2018
@rsc rsc unassigned agl Jun 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. Security
Projects
None yet
Development

No branches or pull requests

5 participants