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/cipher: GCM AEAD Open modifies ciphertext on failure #13886

Closed
rsc opened this issue Jan 8, 2016 · 11 comments
Closed

crypto/cipher: GCM AEAD Open modifies ciphertext on failure #13886

rsc opened this issue Jan 8, 2016 · 11 comments
Milestone

Comments

@rsc
Copy link
Contributor

rsc commented Jan 8, 2016

Debugging a test failure. Have to stop for now but it looks like in Go 1.5 a failure during NewGCM(block).Open(packet, nonce, ciphertext, data) left nonce, ciphertext unchanged. But now in Go 1.6 it looks like they have been modified when the error is returned. Code that tried Open with successive possibilities for data used to be able to reuse the nonce, ciphertext for each call until one succeeded. Now it cannot.

At the least this requires a note in the release notes. Perhaps the old behavior can be restored instead. Will look more later.

/cc @agl

@rsc rsc added this to the Go1.6 milestone Jan 8, 2016
@mdempsky
Copy link
Member

mdempsky commented Jan 9, 2016

Probably https://golang.org/cl/10484 (efeeee3). /cc @vkrasnov

I suspect swapping the order of lines 158--162 and 164--169 of crypto/aes/aes_gcm.go would restore compatibility with Go 1.5. (Edit: No, I'm wrong; it's not that simple.)

@mdempsky
Copy link
Member

mdempsky commented Jan 9, 2016

Assuming I'm looking at the same code you're referring to, it looks like Open probably isn't modifying ciphertext per se, but that the caller is reusing the same memory as the dst argument and expecting Open to not modify dst if the MAC is invalid. Seemingly this is guaranteed by AEAD.Open's documentation:

    // Open decrypts and authenticates ciphertext, authenticates the
    // additional data and, if successful, appends the resulting plaintext
    // to dst, returning the updated slice. [...]
    //
    // The ciphertext and dst may alias exactly or not at all.

It looks like the new AES-GCM code is appending data to dst even when unsuccessful.

@vkrasnov
Copy link
Contributor

vkrasnov commented Jan 9, 2016

It was a design decision to not allow reusing the failed result or partial result, in case the MAC failed, we instead zero the output, to make the usage safer. The scenario where one might reuse ciphertext after failure (not on purpose), is exactly one we wanted to avoid.

@rsc
Copy link
Contributor Author

rsc commented Jan 9, 2016 via email

@vkrasnov
Copy link
Contributor

vkrasnov commented Jan 9, 2016

That was my concern when I submited the patch. The code performs simultaneous decryption and authentication, and the alternative was to copy to a temp buffer.
I consulted @agl and he considered the zeroing to be better, and I agree.
The fact 1.5 did it, does not make it better. I don't think you should restore that behavior. It will be less safe for the intended use, and slower to boot.

@rsc
Copy link
Contributor Author

rsc commented Jan 9, 2016 via email

@vkrasnov
Copy link
Contributor

vkrasnov commented Jan 9, 2016

The more serious issue I see, is that the non amd64 behavior is unchanged, and therefore the same version has different behavior for different architectures.

@mdempsky
Copy link
Member

My 2c: If we're going to accept the new behavior on amd64, we should change the generic GCM code to also zero out dst on MAC failures.

@agl
Copy link
Contributor

agl commented Jan 10, 2016

Since the optimised code stitches together the decryption and authentication for performance, it needs to write the plaintext before it can check whether the authentication tag is correct. Thus, unlike the generic code, it cannot leave the destination buffer untouched in all failure cases.

Since it has written plaintext that it knows is dangerous, zeroing out is, I think, I fair behaviour.

However, it does still leave the destination buffer untouched in other failure cases so it's not the case that it'll always zero it out. BoringSSL does always zero it out but Go is different because it returns a slice and so I'm not immediately convinced that it needs to be the same.

Given that, I think I would like to keep the AESNI behaviour to save a copy, and thus update the generic code to act the same. I'll write some changes to do this.

@gopherbot
Copy link

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

@gopherbot
Copy link

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

gopherbot pushed a commit that referenced this issue Jan 10, 2016
This change documents the behaviour change caused by
https://go-review.googlesource.com/18480 in Go 1.6.

Updates #13886

Change-Id: I2daa08a62775bbc209f0f4cbeae21b8184ce7609
Reviewed-on: https://go-review.googlesource.com/18481
Reviewed-by: Russ Cox <rsc@golang.org>
@golang golang locked and limited conversation to collaborators Jan 13, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants