diff --git a/rc4.go b/rc4.go index db28b61f..94a96b20 100644 --- a/rc4.go +++ b/rc4.go @@ -52,6 +52,9 @@ func (c *RC4Cipher) XORKeyStream(dst, src []byte) { if inexactOverlap(dst[:len(src)], src) { panic("crypto/rc4: invalid buffer overlap") } + // panic if len(dst) < len(src) with a runtime out of bound error, + // which is what crypto/rc4 does. + _ = dst[len(src)-1] var outLen C.int if C.go_openssl_EVP_EncryptUpdate(c.ctx, base(dst), &outLen, base(src), C.int(len(src))) != 1 { panic("crypto/cipher: EncryptUpdate failed") diff --git a/rc4_test.go b/rc4_test.go index e9f8dca9..8182947b 100644 --- a/rc4_test.go +++ b/rc4_test.go @@ -138,6 +138,32 @@ func TestRC4Block(t *testing.T) { } } +func TestRC4OutOfBoundsWrite(t *testing.T) { + if !openssl.SupportsRC4() { + t.Skip("RC4 is not supported") + } + // This cipherText is encrypted "0123456789" + cipherText := []byte{238, 41, 187, 114, 151, 2, 107, 13, 178, 63} + cipher, err := openssl.NewRC4Cipher([]byte{0}) + if err != nil { + panic(err) + } + want := "abcdefghij" + plainText := []byte(want) + shorterLen := len(cipherText) / 2 + defer func() { + err := recover() + if err == nil { + t.Error("XORKeyStream expected to panic on len(dst) < len(src), but didn't") + } + const plain = "0123456789" + if plainText[shorterLen] == plain[shorterLen] { + t.Errorf("XORKeyStream did out of bounds write, want %v, got %v", want, string(plainText)) + } + }() + cipher.XORKeyStream(plainText[:shorterLen], cipherText) +} + func benchmarkRC4(b *testing.B, size int64) { if !openssl.SupportsRC4() { b.Skip("RC4 is not supported")