diff --git a/threefish.go b/threefish.go index d16e82f..e3f88b8 100644 --- a/threefish.go +++ b/threefish.go @@ -20,6 +20,7 @@ package threefish import ( "encoding/binary" "fmt" + "unsafe" ) const ( @@ -64,3 +65,29 @@ func calculateTweak(dst *[(tweakSize / 8) + 1]uint64, src []byte) error { return nil } + +// anyOverlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +// +// This is a copy of crypto/internal/fips140/alias.AnyOverlap, which is not +// importable from outside the standard library. It also exists as +// golang.org/x/crypto/internal/alias.AnyOverlap. +func anyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) && + uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1])) +} + +// inexactOverlap reports whether x and y share memory at any non-corresponding +// index. The memory beyond the slice length is ignored. Note that x and y can +// have different lengths and still not have any inexact overlap. +// +// inexactOverlap can be used to implement the requirements of the +// crypto/cipher Block interface. It is a copy of +// crypto/internal/fips140/alias.InexactOverlap. +func inexactOverlap(x, y []byte) bool { + if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { + return false + } + return anyOverlap(x, y) +} diff --git a/threefish1024.go b/threefish1024.go index 0f4259f..5dd4d4b 100644 --- a/threefish1024.go +++ b/threefish1024.go @@ -66,6 +66,16 @@ func (c *cipher1024) BlockSize() int { return blockSize1024 } // Encrypt loads plaintext from src, encrypts it, and stores it in dst. func (c *cipher1024) Encrypt(dst, src []byte) { + if len(src) < blockSize1024 { + panic("threefish: input not full block") + } + if len(dst) < blockSize1024 { + panic("threefish: output not full block") + } + if inexactOverlap(dst[:blockSize1024], src[:blockSize1024]) { + panic("threefish: invalid buffer overlap") + } + // Load the input in := new([numWords1024]uint64) in[0] = loadWord(src[0:8]) @@ -317,6 +327,16 @@ func (c *cipher1024) Encrypt(dst, src []byte) { // Decrypt loads ciphertext from src, decrypts it, and stores it in dst. func (c *cipher1024) Decrypt(dst, src []byte) { + if len(src) < blockSize1024 { + panic("threefish: input not full block") + } + if len(dst) < blockSize1024 { + panic("threefish: output not full block") + } + if inexactOverlap(dst[:blockSize1024], src[:blockSize1024]) { + panic("threefish: invalid buffer overlap") + } + // Load the ciphertext ct := new([numWords1024]uint64) ct[0] = loadWord(src[0:8]) diff --git a/threefish1024_test.go b/threefish1024_test.go index 71114ac..46727ab 100644 --- a/threefish1024_test.go +++ b/threefish1024_test.go @@ -156,6 +156,71 @@ func TestThreefish1024(t *testing.T) { ) } +func TestThreefish1024Panics(t *testing.T) { + key := make([]byte, blockSize1024) + tweak := make([]byte, tweakSize) + + block, err := New1024(key, tweak) + if err != nil { + t.Fatalf("failed to create cipher with error: %s", err) + } + + full := make([]byte, blockSize1024) + short := make([]byte, blockSize1024-1) + + cases := []struct { + name string + fn func() + }{ + {"encrypt short src", func() { block.Encrypt(full, short) }}, + {"encrypt short dst", func() { block.Encrypt(short, full) }}, + {"decrypt short src", func() { block.Decrypt(full, short) }}, + {"decrypt short dst", func() { block.Decrypt(short, full) }}, + { + "encrypt inexact overlap", + func() { + buf := make([]byte, blockSize1024+1) + block.Encrypt(buf[0:blockSize1024], buf[1:blockSize1024+1]) + }, + }, + { + "decrypt inexact overlap", + func() { + buf := make([]byte, blockSize1024+1) + block.Decrypt(buf[0:blockSize1024], buf[1:blockSize1024+1]) + }, + }, + } + for _, c := range cases { + t.Run( + c.name, + func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("expected a panic but none occurred") + } + }() + c.fn() + }, + ) + } + + // Exact overlap (in-place operation) must remain allowed. + t.Run( + "in-place is allowed", + func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("in-place operation should not panic, got: %v", r) + } + }() + buf := make([]byte, blockSize1024) + block.Encrypt(buf, buf) + block.Decrypt(buf, buf) + }, + ) +} + func BenchmarkThreefish1024(b *testing.B) { key := make([]byte, blockSize1024) tweak := make([]byte, tweakSize) diff --git a/threefish256.go b/threefish256.go index 78f29e6..bd992fa 100644 --- a/threefish256.go +++ b/threefish256.go @@ -66,6 +66,16 @@ func (c *cipher256) BlockSize() int { return blockSize256 } // Encrypt loads plaintext from src, encrypts it, and stores it in dst. func (c *cipher256) Encrypt(dst, src []byte) { + if len(src) < blockSize256 { + panic("threefish: input not full block") + } + if len(dst) < blockSize256 { + panic("threefish: output not full block") + } + if inexactOverlap(dst[:blockSize256], src[:blockSize256]) { + panic("threefish: invalid buffer overlap") + } + // Load the input in := new([numWords256]uint64) in[0] = loadWord(src[0:8]) @@ -153,6 +163,16 @@ func (c *cipher256) Encrypt(dst, src []byte) { // Decrypt loads ciphertext from src, decrypts it, and stores it in dst. func (c *cipher256) Decrypt(dst, src []byte) { + if len(src) < blockSize256 { + panic("threefish: input not full block") + } + if len(dst) < blockSize256 { + panic("threefish: output not full block") + } + if inexactOverlap(dst[:blockSize256], src[:blockSize256]) { + panic("threefish: invalid buffer overlap") + } + // Load the ciphertext ct := new([numWords256]uint64) ct[0] = loadWord(src[0:8]) diff --git a/threefish256_test.go b/threefish256_test.go index 72a7795..74a4648 100644 --- a/threefish256_test.go +++ b/threefish256_test.go @@ -155,6 +155,71 @@ func TestThreefish256(t *testing.T) { ) } +func TestThreefish256Panics(t *testing.T) { + key := make([]byte, blockSize256) + tweak := make([]byte, tweakSize) + + block, err := New256(key, tweak) + if err != nil { + t.Fatalf("failed to create cipher with error: %s", err) + } + + full := make([]byte, blockSize256) + short := make([]byte, blockSize256-1) + + cases := []struct { + name string + fn func() + }{ + {"encrypt short src", func() { block.Encrypt(full, short) }}, + {"encrypt short dst", func() { block.Encrypt(short, full) }}, + {"decrypt short src", func() { block.Decrypt(full, short) }}, + {"decrypt short dst", func() { block.Decrypt(short, full) }}, + { + "encrypt inexact overlap", + func() { + buf := make([]byte, blockSize256+1) + block.Encrypt(buf[0:blockSize256], buf[1:blockSize256+1]) + }, + }, + { + "decrypt inexact overlap", + func() { + buf := make([]byte, blockSize256+1) + block.Decrypt(buf[0:blockSize256], buf[1:blockSize256+1]) + }, + }, + } + for _, c := range cases { + t.Run( + c.name, + func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("expected a panic but none occurred") + } + }() + c.fn() + }, + ) + } + + // Exact overlap (in-place operation) must remain allowed. + t.Run( + "in-place is allowed", + func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("in-place operation should not panic, got: %v", r) + } + }() + buf := make([]byte, blockSize256) + block.Encrypt(buf, buf) + block.Decrypt(buf, buf) + }, + ) +} + func BenchmarkThreefish256(b *testing.B) { key := make([]byte, blockSize256) tweak := make([]byte, tweakSize) diff --git a/threefish512.go b/threefish512.go index e3f11f5..74ce1cc 100644 --- a/threefish512.go +++ b/threefish512.go @@ -66,6 +66,16 @@ func (c *cipher512) BlockSize() int { return blockSize512 } // Encrypt loads plaintext from src, encrypts it, and stores it in dst. func (c *cipher512) Encrypt(dst, src []byte) { + if len(src) < blockSize512 { + panic("threefish: input not full block") + } + if len(dst) < blockSize512 { + panic("threefish: output not full block") + } + if inexactOverlap(dst[:blockSize512], src[:blockSize512]) { + panic("threefish: invalid buffer overlap") + } + // Load the input in := new([numWords512]uint64) in[0] = loadWord(src[0:8]) @@ -205,6 +215,16 @@ func (c *cipher512) Encrypt(dst, src []byte) { // Decrypt loads ciphertext from src, decrypts it, and stores it in dst. func (c *cipher512) Decrypt(dst, src []byte) { + if len(src) < blockSize512 { + panic("threefish: input not full block") + } + if len(dst) < blockSize512 { + panic("threefish: output not full block") + } + if inexactOverlap(dst[:blockSize512], src[:blockSize512]) { + panic("threefish: invalid buffer overlap") + } + // Load the ciphertext ct := new([numWords512]uint64) ct[0] = loadWord(src[0:8]) diff --git a/threefish512_test.go b/threefish512_test.go index d77cb8a..b7f4ff3 100644 --- a/threefish512_test.go +++ b/threefish512_test.go @@ -156,6 +156,71 @@ func TestThreefish512(t *testing.T) { ) } +func TestThreefish512Panics(t *testing.T) { + key := make([]byte, blockSize512) + tweak := make([]byte, tweakSize) + + block, err := New512(key, tweak) + if err != nil { + t.Fatalf("failed to create cipher with error: %s", err) + } + + full := make([]byte, blockSize512) + short := make([]byte, blockSize512-1) + + cases := []struct { + name string + fn func() + }{ + {"encrypt short src", func() { block.Encrypt(full, short) }}, + {"encrypt short dst", func() { block.Encrypt(short, full) }}, + {"decrypt short src", func() { block.Decrypt(full, short) }}, + {"decrypt short dst", func() { block.Decrypt(short, full) }}, + { + "encrypt inexact overlap", + func() { + buf := make([]byte, blockSize512+1) + block.Encrypt(buf[0:blockSize512], buf[1:blockSize512+1]) + }, + }, + { + "decrypt inexact overlap", + func() { + buf := make([]byte, blockSize512+1) + block.Decrypt(buf[0:blockSize512], buf[1:blockSize512+1]) + }, + }, + } + for _, c := range cases { + t.Run( + c.name, + func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("expected a panic but none occurred") + } + }() + c.fn() + }, + ) + } + + // Exact overlap (in-place operation) must remain allowed. + t.Run( + "in-place is allowed", + func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("in-place operation should not panic, got: %v", r) + } + }() + buf := make([]byte, blockSize512) + block.Encrypt(buf, buf) + block.Decrypt(buf, buf) + }, + ) +} + func BenchmarkThreefish512(b *testing.B) { key := make([]byte, blockSize512) tweak := make([]byte, tweakSize)