-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
webtransport: use deterministic TLS certificates #1833
Conversation
runs := 1000 | ||
for i := 0; i < runs; i++ { | ||
t.Run(fmt.Sprintf("Run=%d", i), func(t *testing.T) { | ||
zeroSeed := [32]byte{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a random value? Otherwise, what's the point of running 1000 runs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't matter. The test is to check if the signing process itself is non-deterministic. This seed is fed into the hkdf so the signing process is oblivious to this.
Imagine the signing process changes in the future and it randomly reads a byte from /dev/urandom
to mix in 10% of the time. Then a single run would succeed 81% of the time. Doing more runs decreases the chance of success iteratively (0.81*0.81*...)
. The 1000 runs is to detect if there's any chance on non-determinism.
runs := 1000 | ||
for i := 0; i < runs; i++ { | ||
t.Run(fmt.Sprintf("Run=%d", i), func(t *testing.T) { | ||
zeroSeed := [32]byte{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
@@ -77,7 +86,7 @@ func TestCertRenewal(t *testing.T) { | |||
} | |||
return false | |||
}, 100*time.Millisecond, 10*time.Millisecond) | |||
cl.Add(2 * time.Second) | |||
cl.Add(clockSkewAllowance + 2*time.Second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which change in the logic made this necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need the skew here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kind of. In this test we have this setup
v-- start here-ish
|---------|
|---------|
with cl.Set(m.currentConfig.End().Add(-(2*clockSkewAllowance + time.Second)))
, we advance the clock:
v-- clock is here now (minus one second)
|---------|
|---------|
Remember the transition point is one clock skew before the end.
v-- clock is here now (minus one second)
|---------|
| <- transition point
|---------|
So we need to move one clock skew forward and a couple seconds
v-- clock is here now (plus one second)
|---------|
| <- transition point
|---------|
Maybe this makes more sense if I change it to only advance the clock to one clock skew before the end?
with cl.Set(m.currentConfig.End().Add(-(clockSkewAllowance + time.Second)))
, we advance the clock:
c387a22
to
92bcb04
Compare
for i := 0; i < runs; i++ { | ||
t.Run(fmt.Sprintf("Run=%d", i), func(t *testing.T) { | ||
cl := clock.NewMock() | ||
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should use a zero byte seed. Otherwise we can randomly get to an edge case around the hour (with random offset)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand. Isn’t there the chance we hit the offset for any key?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test makes sure that if we reboot one hour in the future then we get the same key assuming we aren't near the boundary. The problem with the random key is that it means we have a random offset (offset is defined by the key). So by setting the key to a predetermined one we can uphold the assumption. The zero seed is just one such key, but any key that doesn't put us near the bounds is fine.
// We want to add a random offset to each start time so that not all certs | ||
// rotate at the same time across the network. The offset represents moving | ||
// the bucket start time some `offset` earlier. | ||
offset := (time.Duration(binary.LittleEndian.Uint16(pubkeyBytes)) * time.Minute) % (certValidity - clockSkewAllowance) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this module certValidity minus a single clockSkewAllowance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was including the clockSkew allowance into the offset, but that was wrong (and also tricky). I've done the simpler thing of subtracting the start
I pass to getCurrentBucketStartTime
by clockSkewAllowance
. This works because getCurrentBucketStartTime
will never give a time later than start + certValidity - 2*clockSkew
.
@@ -77,7 +86,7 @@ func TestCertRenewal(t *testing.T) { | |||
} | |||
return false | |||
}, 100*time.Millisecond, 10*time.Millisecond) | |||
cl.Add(2 * time.Second) | |||
cl.Add(clockSkewAllowance + 2*time.Second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need the skew here?
@@ -100,3 +110,66 @@ func TestCertRenewal(t *testing.T) { | |||
// check that the 2nd certificate from the beginning was rolled over to be the 1st certificate | |||
require.Equal(t, second[1].Value(), third[0].Value()) | |||
} | |||
|
|||
func TestDeterministicCertsAcrossReboots(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to skip on OSX?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MacOS issue was around listening, this should be okay (after using a deterministic seed, since otherwise we have random offsets and adding an hour might get us over the border).
for i := 0; i < runs; i++ { | ||
t.Run(fmt.Sprintf("Run=%d", i), func(t *testing.T) { | ||
cl := clock.NewMock() | ||
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand. Isn’t there the chance we hit the offset for any key?
func TestServerSendsBackValidCert(t *testing.T) { | ||
var maxTimeoutErrors = 10 | ||
|
||
require.NoError(t, quick.Check(func(timeSinceUnixEpoch time.Duration, keySeed int64, randomClientSkew time.Duration) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to repeat this test 100 time or so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the default: https://pkg.go.dev/testing/quick?utm_source=gopls#Config
Looks like one of these tests is failing:
|
e7455e4
to
e913a1b
Compare
Fixes the logic that a cert has been valid for a clockSkew by subtracting the clockSkew from the start time rather than incorporating it into the offset. The offset should be used to shift the buckets.
e913a1b
to
3387eb7
Compare
Closes #1824