|
|
@@ -8,7 +8,12 @@ import (
|
|
|
"bytes"
|
|
|
"crypto"
|
|
|
"crypto/rand"
|
|
|
+ "encoding/binary"
|
|
|
+ "io"
|
|
|
"testing"
|
|
|
+
|
|
|
+ "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/poly1305"
|
|
|
+ "golang.org/x/crypto/chacha20"
|
|
|
)
|
|
|
|
|
|
func TestDefaultCiphersExist(t *testing.T) {
|
|
|
@@ -129,3 +134,98 @@ func TestCBCOracleCounterMeasure(t *testing.T) {
|
|
|
lastRead = bytesRead
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+func TestCVE202143565(t *testing.T) {
|
|
|
+ tests := []struct {
|
|
|
+ cipher string
|
|
|
+ constructPacket func(packetCipher) io.Reader
|
|
|
+ }{
|
|
|
+ {
|
|
|
+ cipher: gcmCipherID,
|
|
|
+ constructPacket: func(client packetCipher) io.Reader {
|
|
|
+ internalCipher := client.(*gcmCipher)
|
|
|
+ b := &bytes.Buffer{}
|
|
|
+ prefix := [4]byte{}
|
|
|
+ if _, err := b.Write(prefix[:]); err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+ internalCipher.buf = internalCipher.aead.Seal(internalCipher.buf[:0], internalCipher.iv, []byte{}, prefix[:])
|
|
|
+ if _, err := b.Write(internalCipher.buf); err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+ internalCipher.incIV()
|
|
|
+
|
|
|
+ return b
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ cipher: chacha20Poly1305ID,
|
|
|
+ constructPacket: func(client packetCipher) io.Reader {
|
|
|
+ internalCipher := client.(*chacha20Poly1305Cipher)
|
|
|
+ b := &bytes.Buffer{}
|
|
|
+
|
|
|
+ nonce := make([]byte, 12)
|
|
|
+ s, err := chacha20.NewUnauthenticatedCipher(internalCipher.contentKey[:], nonce)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+ var polyKey, discardBuf [32]byte
|
|
|
+ s.XORKeyStream(polyKey[:], polyKey[:])
|
|
|
+ s.XORKeyStream(discardBuf[:], discardBuf[:]) // skip the next 32 bytes
|
|
|
+
|
|
|
+ internalCipher.buf = make([]byte, 4+poly1305.TagSize)
|
|
|
+ binary.BigEndian.PutUint32(internalCipher.buf, 0)
|
|
|
+ ls, err := chacha20.NewUnauthenticatedCipher(internalCipher.lengthKey[:], nonce)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+ ls.XORKeyStream(internalCipher.buf, internalCipher.buf[:4])
|
|
|
+ if _, err := io.ReadFull(rand.Reader, internalCipher.buf[4:4]); err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ s.XORKeyStream(internalCipher.buf[4:], internalCipher.buf[4:4])
|
|
|
+
|
|
|
+ var tag [poly1305.TagSize]byte
|
|
|
+ poly1305.Sum(&tag, internalCipher.buf[:4], &polyKey)
|
|
|
+
|
|
|
+ copy(internalCipher.buf[4:], tag[:])
|
|
|
+
|
|
|
+ if _, err := b.Write(internalCipher.buf); err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ return b
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, tc := range tests {
|
|
|
+ mac := "hmac-sha2-256"
|
|
|
+
|
|
|
+ kr := &kexResult{Hash: crypto.SHA1}
|
|
|
+ algs := directionAlgorithms{
|
|
|
+ Cipher: tc.cipher,
|
|
|
+ MAC: mac,
|
|
|
+ Compression: "none",
|
|
|
+ }
|
|
|
+ client, err := newPacketCipher(clientKeys, algs, kr)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("newPacketCipher(client, %q, %q): %v", tc.cipher, mac, err)
|
|
|
+ }
|
|
|
+ server, err := newPacketCipher(clientKeys, algs, kr)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("newPacketCipher(client, %q, %q): %v", tc.cipher, mac, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ b := tc.constructPacket(client)
|
|
|
+
|
|
|
+ wantErr := "ssh: empty packet"
|
|
|
+ _, err = server.readCipherPacket(0, b)
|
|
|
+ if err == nil {
|
|
|
+ t.Fatalf("readCipherPacket(%q, %q): didn't fail with empty packet", tc.cipher, mac)
|
|
|
+ } else if err.Error() != wantErr {
|
|
|
+ t.Fatalf("readCipherPacket(%q, %q): unexpected error, got %q, want %q", tc.cipher, mac, err, wantErr)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|