// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cryptotest

import (
	
	
	
)

type MakeBlock func(key []byte) (cipher.Block, error)

// TestBlock performs a set of tests on cipher.Block implementations, checking
// the documented requirements of BlockSize, Encrypt, and Decrypt.
func ( *testing.T,  int,  MakeBlock) {
	// Generate random key
	 := make([]byte, )
	newRandReader().Read()
	.Logf("Cipher key: 0x%x", )

	,  := ()
	if  != nil {
		.Fatal()
	}

	 := .BlockSize()

	.Run("Encryption", func( *testing.T) {
		testCipher(, .Encrypt, )
	})

	.Run("Decryption", func( *testing.T) {
		testCipher(, .Decrypt, )
	})

	// Checks baseline Encrypt/Decrypt functionality.  More thorough
	// implementation-specific characterization/golden tests should be done
	// for each block cipher implementation.
	.Run("Roundtrip", func( *testing.T) {
		 := newRandReader()

		// Check Decrypt inverts Encrypt
		, ,  := make([]byte, ), make([]byte, ), make([]byte, )

		.Read()

		.Encrypt(, )
		.Decrypt(, )

		if !bytes.Equal(, ) {
			.Errorf("plaintext is different after an encrypt/decrypt cycle; got %x, want %x", , )
		}

		// Check Encrypt inverts Decrypt (assumes block ciphers are deterministic)
		, ,  := make([]byte, ), make([]byte, ), make([]byte, )

		.Read()

		.Decrypt(, )
		.Encrypt(, )

		if !bytes.Equal(, ) {
			.Errorf("ciphertext is different after a decrypt/encrypt cycle; got %x, want %x", , )
		}
	})

}

func testCipher( *testing.T,  func(,  []byte),  int) {
	.Run("AlterInput", func( *testing.T) {
		 := newRandReader()

		// Make long src that shouldn't be modified at all, within block
		// size scope or beyond it
		,  := make([]byte, *2), make([]byte, *2)
		.Read()
		copy(, )

		 := make([]byte, )

		(, )
		if !bytes.Equal(, ) {
			.Errorf("block cipher modified src; got %x, want %x", , )
		}
	})

	.Run("Aliasing", func( *testing.T) {
		 := newRandReader()

		,  := make([]byte, ), make([]byte, )

		// Record what output is when src and dst are different
		.Read()
		(, )

		// Check that the same output is generated when src=dst alias to the same
		// memory
		(, )
		if !bytes.Equal(, ) {
			.Errorf("block cipher produced different output when dst = src; got %x, want %x", , )
		}
	})

	.Run("OutOfBoundsWrite", func( *testing.T) {
		 := newRandReader()

		 := make([]byte, )
		.Read()

		// Make a buffer with dst in the middle and data on either end
		 := make([]byte, *3)
		,  := , *2
		.Read([:])
		.Read([:])
		 := [:]

		// Record the prefix and suffix data to make sure they aren't written to
		,  := make([]byte, ), make([]byte, )
		copy(, [:])
		copy(, [:])

		// Write to dst (the middle of the buffer) and make sure it doesn't write
		// beyond the dst slice
		(, )
		if !bytes.Equal([:], ) {
			.Errorf("block cipher did out of bounds write after end of dst slice; got %x, want %x", [:], )
		}
		if !bytes.Equal([:], ) {
			.Errorf("block cipher did out of bounds write before beginning of dst slice; got %x, want %x", [:], )
		}

		// Check that dst isn't written to beyond BlockSize even if there is room
		// in the slice
		 = [:] // Extend dst to include suffix
		(, )
		if !bytes.Equal([:], ) {
			.Errorf("block cipher modified dst past BlockSize bytes; got %x, want %x", [:], )
		}
	})

	// Check that output of cipher isn't affected by adjacent data beyond input
	// slice scope
	// For encryption, this assumes block ciphers encrypt deterministically
	.Run("OutOfBoundsRead", func( *testing.T) {
		 := newRandReader()

		 := make([]byte, )
		.Read()
		 := make([]byte, )
		(, )

		// Make a buffer with src in the middle and data on either end
		 := make([]byte, *3)
		,  := , *2

		copy([:], )
		.Read([:])
		.Read([:])

		 := make([]byte, )
		(, [:])
		if !bytes.Equal(, ) {
			.Errorf("block cipher affected by data outside of src slice bounds; got %x, want %x", , )
		}

		// Check that src isn't read from beyond BlockSize even if the slice is
		// longer and contains data in the suffix
		(, [:]) // Input long src
		if !bytes.Equal(, ) {
			.Errorf("block cipher affected by src data beyond BlockSize bytes; got %x, want %x", [:], )
		}
	})

	.Run("NonZeroDst", func( *testing.T) {
		 := newRandReader()

		// Record what the cipher writes into a destination of zeroes
		 := make([]byte, )
		.Read()
		 := make([]byte, )

		(, )

		// Make nonzero dst
		 := make([]byte, *2)
		.Read()

		// Remember the random suffix which shouldn't be written to
		 = append(, [:]...)

		(, )
		if !bytes.Equal(, ) {
			.Errorf("block cipher behavior differs when given non-zero dst; got %x, want %x", , )
		}
	})

	.Run("BufferOverlap", func( *testing.T) {
		 := newRandReader()

		 := make([]byte, *2)
		.Read(())

		// Make src and dst slices point to same array with inexact overlap
		 := [:]
		 := [1 : +1]
		mustPanic(, "invalid buffer overlap", func() { (, ) })

		// Only overlap on one byte
		 = [:]
		 = [-1 : 2*-1]
		mustPanic(, "invalid buffer overlap", func() { (, ) })

		// src comes after dst with one byte overlap
		 = [-1 : 2*-1]
		 = [:]
		mustPanic(, "invalid buffer overlap", func() { (, ) })
	})

	// Test short input/output.
	// Assembly used to not notice.
	// See issue 7928.
	.Run("ShortBlock", func( *testing.T) {
		// Returns slice of n bytes of an n+1 length array.  Lets us test that a
		// slice is still considered too short even if the underlying array it
		// points to is large enough
		 := func( int) []byte { return make([]byte, +1)[0:] }

		// Off by one byte
		mustPanic(, "input not full block", func() { ((), (-1)) })
		mustPanic(, "output not full block", func() { ((-1), ()) })

		// Small slices
		mustPanic(, "input not full block", func() { ((1), (1)) })
		mustPanic(, "input not full block", func() { ((100), (1)) })
		mustPanic(, "output not full block", func() { ((1), (100)) })
	})
}

func mustPanic( *testing.T,  string,  func()) {
	.Helper()

	defer func() {
		.Helper()

		 := recover()

		if  == nil {
			.Errorf("function did not panic for %q", )
		}
	}()
	()
}