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

x/crypto/ssh/agent: non-constant time passphrase check #25173

Closed
weingart opened this issue Apr 29, 2018 · 12 comments
Closed

x/crypto/ssh/agent: non-constant time passphrase check #25173

weingart opened this issue Apr 29, 2018 · 12 comments
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Milestone

Comments

@weingart
Copy link

Please answer these questions before submitting your issue. Thanks!

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

1.10.1

Does this issue reproduce with the latest release?

Yes

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

What did you do?

Look at: https://github.com/golang/crypto/blob/master/ssh/agent/keyring.go#L105

What did you expect to see?

A comparison that was constant time.

What did you see instead?

A comparison that could be used to divine the length of the passphrase.

How to fix?

Use a constant time comparison, without a length check short-circuit.

Other option:
Stored a hashed via bcrypt (or similar) version of the passphrase instead. The comparison
should then also hash the attempted passphrase and use a constant time compare (which the
hash would pretty much guarantee). Note, this could make the implementation also more safe
from casual memory snooping.

@gopherbot gopherbot added this to the Unreleased milestone Apr 29, 2018
@ALTree ALTree added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Apr 29, 2018
@ALTree
Copy link
Member

ALTree commented Apr 29, 2018

Use a constant time comparison, without a length check short-circuit

I don't think it's possible to constant-time compare two arrays of different lengths.

cc @agl @FiloSottile to decide if this issue is actionable.

@as
Copy link
Contributor

as commented Apr 29, 2018

@weingart

It's true that hashing the passphrase will create a bitstring comparable in constant time, but the suggestion of bcrypt sounds rather expensive. Is there a particular reason you would choose this function over another?

Note, this could make the implementation also more safe
from casual memory snooping.

I would prefer to step away from such corollaries, it creates one more location in memory associated with the passphrase.

@nsajko
Copy link
Contributor

nsajko commented Apr 29, 2018

I don't think it's possible to constant-time compare two arrays of different lengths.

Would something like [0], but written in Go assembly, not be constant-time? It is supposed to be constant time with respect to length of one string (the attacker controlled one).
[0] https://nachtimwald.com/2017/04/02/constant-time-string-comparison-in-c/

@odeke-em odeke-em changed the title x/crypto/ssh/agent: Non-constant passphrase check x/crypto/ssh/agent: non-constant time passphrase check Apr 30, 2018
@odeke-em
Copy link
Member

/cc @hanwen too

@hanwen
Copy link
Contributor

hanwen commented Apr 30, 2018

the bug here is that we're checking the lengths at all, as ConstantTimeCompare already does so

https://golang.org/src/crypto/subtle/constant_time.go?s=501:542#L2

see also https://go.googlesource.com/go/+/446bfffcd6cfcaca141988a9d844aac61a704866 and issue
#7304 (which coincidentally, I filed.)

@gopherbot
Copy link

Change https://golang.org/cl/110068 mentions this issue: ssh/agent: remove len check in Unlock

@ALTree
Copy link
Member

ALTree commented Apr 30, 2018

the bug here is that we're checking the lengths at all, as ConstantTimeCompare already does so

I've sent a fix for this while we wait for a decision regarding making Unlock constant-time even if the arrays have different length.

gopherbot pushed a commit to golang/crypto that referenced this issue Apr 30, 2018
Unlock compares the length of the passphrase with the given one before
calling subtle.ConstantTimeCompare. This is redundant, since
ConstantTimeCompare already perform a lengths check before doing
anything. Remove the check from Unlock.

Updates golang/go#25173

Change-Id: Ib5fec3a94392bddf2996f5c6bf5a414529e86f2f
Reviewed-on: https://go-review.googlesource.com/110068
Run-TryBot: Alberto Donizetti <alb.donizetti@gmail.com>
Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
@hanwen
Copy link
Contributor

hanwen commented Apr 30, 2018

I've submitted the aforementioned change.

I don't think there is anything left to be done here.

@hanwen hanwen closed this as completed Apr 30, 2018
@nsajko
Copy link
Contributor

nsajko commented Apr 30, 2018

But the submitted fix does not address the issue: that comparing two strings of different lengths with subtle.ConstantTimeCompare takes less time than comparing two strings of equal length. Is the code I linked to not correct?

@hanwen
Copy link
Contributor

hanwen commented Apr 30, 2018

strings in Go are not null terminated, so the code you link to can't work.

you could make subtle.ConstantTimeCompare constant time for differently sized input strings, eg. by doing

l :=  len(x) - len(y)
var bs [4]byte
binary.PutInt(l, bs[:])
byte v = bs[0] | bs[1] | bs[2] | bs[3]
for i := 0; i < len(x) && i < len(y); i++ {
  v |= x[i]^y[i]
}

but it will still leak the size of the smallest array, and it takes work to do this portably.

@josharian
Copy link
Contributor

I am not a cryptographer, but it seems to me that the linked code could be ported straightforwardly to Go by replacing "byte is NULL" by "current index >= length of string".

@nsajko
Copy link
Contributor

nsajko commented May 1, 2018

I am not a cryptographer, but it seems to me that the linked code could be ported straightforwardly to Go by replacing "byte is NULL" by "current index >= length of string".

It is not that simple, just doing that would result in a read out of slice bounds in the first line of the loop.

@golang golang locked and limited conversation to collaborators May 1, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Projects
None yet
Development

No branches or pull requests

8 participants