Recover UDP crypto after packet loss
This commit is contained in:
committed by
Brandon McGinty
parent
90f8a1ca5f
commit
4510c25350
+16
-3
@@ -292,10 +292,13 @@ func (cs *cryptState15) decrypt15(packet []byte) ([]byte, error) {
|
|||||||
backupIV(cs.decryptIV[:])
|
backupIV(cs.decryptIV[:])
|
||||||
restore = true
|
restore = true
|
||||||
} else if ivByte > cs.decryptIV[0] && diff > 0 {
|
} else if ivByte > cs.decryptIV[0] && diff > 0 {
|
||||||
// We missed packets; catch up. Already handled above.
|
// We missed packets; move the low IV byte forward.
|
||||||
|
cs.decryptIV[0] = ivByte
|
||||||
} else if ivByte < cs.decryptIV[0] && diff > 0 {
|
} else if ivByte < cs.decryptIV[0] && diff > 0 {
|
||||||
// Wrapped forward; advance and catch up.
|
// We missed packets across a low-byte wrap. The IV's higher
|
||||||
advanceIV(cs.decryptIV[:])
|
// bytes must advance even though the received low byte is set
|
||||||
|
// below rather than incremented.
|
||||||
|
advanceIVHighBytes(cs.decryptIV[:])
|
||||||
cs.decryptIV[0] = ivByte
|
cs.decryptIV[0] = ivByte
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("gumble: OCB IV too far off")
|
return nil, errors.New("gumble: OCB IV too far off")
|
||||||
@@ -340,6 +343,16 @@ func advanceIV(iv []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// advanceIVHighBytes advances all but the low IV byte as a little-endian integer.
|
||||||
|
func advanceIVHighBytes(iv []byte) {
|
||||||
|
for i := 1; i < len(iv); i++ {
|
||||||
|
iv[i]++
|
||||||
|
if iv[i] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// backupIV decrements a 16-byte IV as a little-endian integer.
|
// backupIV decrements a 16-byte IV as a little-endian integer.
|
||||||
func backupIV(iv []byte) {
|
func backupIV(iv []byte) {
|
||||||
for i := 0; i < len(iv); i++ {
|
for i := 0; i < len(iv); i++ {
|
||||||
|
|||||||
@@ -32,6 +32,75 @@ func TestAdvanceIV(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Regression coverage for the native IV carry path at the 255->256 wrap.
|
// Regression coverage for the native IV carry path at the 255->256 wrap.
|
||||||
|
func TestCryptState15DecryptsAfterMissedPackets(t *testing.T) {
|
||||||
|
key := mustDecodeHex("93360b0f86a926c4561563469026eb94")
|
||||||
|
nonce := mustDecodeHex("10000000000000000000000000000000")
|
||||||
|
out, in := &cryptState15{}, &cryptState15{}
|
||||||
|
if err := out.setup15(key, nonce, nonce); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := in.setup15(key, nonce, nonce); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
first, err := out.encrypt15([]byte("first"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := in.decrypt15(first); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
if _, err := out.encrypt15([]byte("dropped")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last, err := out.encrypt15([]byte("after loss"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
plain, err := in.decrypt15(last)
|
||||||
|
if err != nil || !bytes.Equal(plain, []byte("after loss")) {
|
||||||
|
t.Fatalf("decrypt after missed packets = %q, %v", plain, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCryptState15DecryptsAfterMissedPacketsAcrossIVByteWrap(t *testing.T) {
|
||||||
|
key := mustDecodeHex("93360b0f86a926c4561563469026eb94")
|
||||||
|
nonce := mustDecodeHex("fa000000000000000000000000000000")
|
||||||
|
out, in := &cryptState15{}, &cryptState15{}
|
||||||
|
if err := out.setup15(key, nonce, nonce); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := in.setup15(key, nonce, nonce); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
first, err := out.encrypt15([]byte("before wrap"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := in.decrypt15(first); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
if _, err := out.encrypt15([]byte("dropped")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last, err := out.encrypt15([]byte("after wrap"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
plain, err := in.decrypt15(last)
|
||||||
|
if err != nil || !bytes.Equal(plain, []byte("after wrap")) {
|
||||||
|
t.Fatalf("decrypt after missed packets across IV wrap = %q, %v", plain, err)
|
||||||
|
}
|
||||||
|
if in.decryptIV[0] != 2 || in.decryptIV[1] != 1 {
|
||||||
|
t.Fatalf("unexpected IV after wrapped loss: %x", in.decryptIV[:2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCryptState15DecryptsAcrossIVByteWrap(t *testing.T) {
|
func TestCryptState15DecryptsAcrossIVByteWrap(t *testing.T) {
|
||||||
key := mustDecodeHex("93360b0f86a926c4561563469026eb94")
|
key := mustDecodeHex("93360b0f86a926c4561563469026eb94")
|
||||||
clientNonce := mustDecodeHex("ff000000000000000000000000000000")
|
clientNonce := mustDecodeHex("ff000000000000000000000000000000")
|
||||||
|
|||||||
Reference in New Issue
Block a user