// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compositemldsa_test

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/tink-crypto/tink-go/v2/internal/outputprefix"
	internalcompmldsa "github.com/tink-crypto/tink-go/v2/internal/signature/compositemldsa"
	compmldsatestvectors "github.com/tink-crypto/tink-go/v2/internal/signature/compositemldsa/testing"
	"github.com/tink-crypto/tink-go/v2/key"
	"github.com/tink-crypto/tink-go/v2/signature/compositemldsa"
	"github.com/tink-crypto/tink-go/v2/signature/ecdsa"
	"github.com/tink-crypto/tink-go/v2/signature/ed25519"
	"github.com/tink-crypto/tink-go/v2/signature/rsassapkcs1"
	"github.com/tink-crypto/tink-go/v2/signature/rsassapss"
)

const (
	f4 = 65537
)

func TestNewParametersSupported(t *testing.T) {
	for _, tc := range compmldsatestvectors.TestCasesSupportedParameters(t) {
		classicalAlgorithm := compositemldsa.ClassicalAlgorithm(tc.ClassicalAlgorithm)
		instance := compositemldsa.MLDSAInstance(tc.Instance)
		variant := compositemldsa.Variant(tc.Variant)
		params, err := compositemldsa.NewParameters(classicalAlgorithm, instance, variant)
		if err != nil {
			t.Errorf("compositemldsa.NewParameters(%v, %v, %v) err = %v, want nil", classicalAlgorithm, instance, variant, err)
		}
		if got := params.ClassicalAlgorithm(); got != classicalAlgorithm {
			t.Errorf("params.ClassicalAlgorithm() = %v, want %v", got, classicalAlgorithm)
		}
		if got := params.MLDSAInstance(); got != instance {
			t.Errorf("params.MlDsaInstance() = %v, want %v", got, instance)
		}
		if got := params.Variant(); got != variant {
			t.Errorf("params.Variant() = %v, want %v", got, variant)
		}
	}
}

func TestNewParametersUnsupported(t *testing.T) {
	tests := []struct {
		classicalAlgorithm compositemldsa.ClassicalAlgorithm
		instance           compositemldsa.MLDSAInstance
		variant            compositemldsa.Variant
	}{
		// Unknown
		{compositemldsa.UnknownAlgorithm, compositemldsa.MLDSA65, compositemldsa.VariantTink},
		{compositemldsa.Ed25519, compositemldsa.UnknownInstance, compositemldsa.VariantTink},
		{compositemldsa.Ed25519, compositemldsa.MLDSA65, compositemldsa.VariantUnknown},
		// MLDSA65 unsupported
		{compositemldsa.ECDSAP521, compositemldsa.MLDSA65, compositemldsa.VariantTink},
		// MLDSA87 unsupported
		{compositemldsa.Ed25519, compositemldsa.MLDSA87, compositemldsa.VariantTink},
		{compositemldsa.ECDSAP256, compositemldsa.MLDSA87, compositemldsa.VariantTink},
		{compositemldsa.RSA3072PKCS1, compositemldsa.MLDSA87, compositemldsa.VariantTink},
		{compositemldsa.RSA4096PKCS1, compositemldsa.MLDSA87, compositemldsa.VariantTink},
	}
	for _, tc := range tests {
		_, err := compositemldsa.NewParameters(tc.classicalAlgorithm, tc.instance, tc.variant)
		if err == nil {
			t.Errorf("compositemldsa.NewParameters(%v, %v, %v) err = nil, want error", tc.classicalAlgorithm, tc.instance, tc.variant)
		}
	}
}

func TestParametersEqual(t *testing.T) {
	for _, classicalAlgorithm := range []compositemldsa.ClassicalAlgorithm{
		compositemldsa.RSA3072PSS,
		compositemldsa.ECDSAP384,
	} {
		for _, instance := range []compositemldsa.MLDSAInstance{
			compositemldsa.MLDSA65,
			compositemldsa.MLDSA87,
		} {
			t.Run(fmt.Sprintf("%v/%v", classicalAlgorithm, instance), func(t *testing.T) {
				tinkParams, err := compositemldsa.NewParameters(classicalAlgorithm, instance, compositemldsa.VariantTink)
				if err != nil {
					t.Fatalf("NewParameters(classicalAlgorithm, instance, compositemldsa.VariantTink) err = %v", err)
				}
				noPrefixParams, err := compositemldsa.NewParameters(classicalAlgorithm, instance, compositemldsa.VariantNoPrefix)
				if err != nil {
					t.Fatalf("NewParameters(classicalAlgorithm, instance, compositemldsa.VariantNoPrefix) err = %v", err)
				}
				if !tinkParams.Equal(tinkParams) {
					t.Errorf("tinkParams.Equal(tinkParams) = false, want true")
				}
				if !noPrefixParams.Equal(noPrefixParams) {
					t.Errorf("noPrefixParams.Equal(noPrefixParams) = false, want true")
				}
				if tinkParams.Equal(noPrefixParams) {
					t.Errorf("tinkParams.Equal(noPrefixParams) = true, want false")
				}
			})
		}
	}
	// Test inequality for different classical algorithms and instances.
	p1, err := compositemldsa.NewParameters(compositemldsa.Ed25519, compositemldsa.MLDSA65, compositemldsa.VariantTink)
	if err != nil {
		t.Fatalf("%v", err)
	}
	p2, err := compositemldsa.NewParameters(compositemldsa.ECDSAP384, compositemldsa.MLDSA65, compositemldsa.VariantTink)
	if err != nil {
		t.Fatalf("%v", err)
	}
	if p1.Equal(p2) {
		t.Errorf("p1.Equal(p2) = true, want false")
	}
	p3, err := compositemldsa.NewParameters(compositemldsa.ECDSAP384, compositemldsa.MLDSA87, compositemldsa.VariantTink)
	if err != nil {
		t.Fatalf("%v", err)
	}
	if p2.Equal(p3) {
		t.Errorf("p1.Equal(p3) = true, want false")
	}
}

func TestNewPublicKeySuccess(t *testing.T) {
	for _, tc := range compmldsatestvectors.TestCasesSupportedParameters(t) {
		classicalAlgorithm := compositemldsa.ClassicalAlgorithm(tc.ClassicalAlgorithm)
		instance := compositemldsa.MLDSAInstance(tc.Instance)
		variant := compositemldsa.Variant(tc.Variant)
		testName := fmt.Sprintf("%v-%v-%v", classicalAlgorithm, instance, variant)
		t.Run(testName, func(t *testing.T) {
			params, err := compositemldsa.NewParameters(classicalAlgorithm, instance, variant)
			if err != nil {
				t.Fatalf("compositemldsa.NewParameters(%v, %v, %v) err = %v, want nil", classicalAlgorithm, instance, variant, err)
			}
			// We don't need the private keys for this test.
			_, mlDsaPubKey := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(instance))
			_, classicalPubKey := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(classicalAlgorithm), tc.ClassicalParams)
			const keyID = uint32(0x12345678)
			pubKey, err := compositemldsa.NewPublicKey(mlDsaPubKey, classicalPubKey, keyID, params)
			if err != nil {
				t.Errorf("compositemldsa.NewPublicKey(%v, %v, %v, %v) err = %v, want nil", mlDsaPubKey, classicalPubKey, keyID, params, err)
			}
			if !pubKey.Parameters().Equal(params) {
				t.Errorf("pubKey.Parameters() = %v, want %v", pubKey.Parameters(), params)
			}
			if gotID, gotOK := pubKey.IDRequirement(); gotOK != params.HasIDRequirement() || (gotOK && gotID != keyID) {
				t.Errorf("pubKey.IDRequirement() = %v, %v, want %v, %v", gotID, gotOK, keyID, params.HasIDRequirement())
			}
			if !pubKey.ClassicalPublicKey().Equal(classicalPubKey) {
				t.Errorf("pubKey.ClassicalPublicKey() = %v, want %v", pubKey.ClassicalPublicKey(), classicalPubKey)
			}
			if !pubKey.MLDSAPublicKey().Equal(mlDsaPubKey) {
				t.Errorf("pubKey.MLDSAPublicKey() = %v, want %v", pubKey.MLDSAPublicKey(), mlDsaPubKey)
			}
			var expectedOutputPrefix []byte
			if params.Variant() == compositemldsa.VariantTink {
				expectedOutputPrefix = outputprefix.Tink(keyID)
			}
			if !bytes.Equal(pubKey.OutputPrefix(), expectedOutputPrefix) {
				t.Errorf("pubKey.OutputPrefix() = %v, want %v", pubKey.OutputPrefix(), expectedOutputPrefix)
			}
		})
	}
}

func TestNewPublicKeyEquals(t *testing.T) {
	for _, tc := range compmldsatestvectors.TestCasesSupportedParameters(t) {
		classicalAlgorithm := compositemldsa.ClassicalAlgorithm(tc.ClassicalAlgorithm)
		instance := compositemldsa.MLDSAInstance(tc.Instance)
		variant := compositemldsa.Variant(tc.Variant)
		testName := fmt.Sprintf("%v-%v-%v", classicalAlgorithm, instance, variant)
		t.Run(testName, func(t *testing.T) {
			params, err := compositemldsa.NewParameters(classicalAlgorithm, instance, variant)
			if err != nil {
				t.Fatalf("compositemldsa.NewParameters(%v, %v, %v) err = %v, want nil", classicalAlgorithm, instance, variant, err)
			}
			_, mlDsaPubKey := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(instance))
			_, classicalPubKey := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(classicalAlgorithm), tc.ClassicalParams)
			keyID := uint32(0x12345678)
			pubKey, err := compositemldsa.NewPublicKey(mlDsaPubKey, classicalPubKey, keyID, params)
			if err != nil {
				t.Fatalf("compositemldsa.NewPublicKey(%v, %v, %v, %v) err = %v, want nil", mlDsaPubKey, classicalPubKey, keyID, params, err)
			}
			if !pubKey.Equal(pubKey) {
				t.Errorf("pubKey.Equal(pubKey) = false, want true")
			}
		})
	}
}

func TestNewPublicKeyMismatchClassicalParameters(t *testing.T) {
	tests := []struct {
		name               string
		classicalAlgorithm compositemldsa.ClassicalAlgorithm
		instance           compositemldsa.MLDSAInstance
		variant            compositemldsa.Variant
		keyAlgorithm       compositemldsa.ClassicalAlgorithm
		classicalParams    key.Parameters
	}{
		{
			name:               "Ed25519 params with ECDSAP256 key",
			classicalAlgorithm: compositemldsa.Ed25519,
			instance:           compositemldsa.MLDSA65,
			variant:            compositemldsa.VariantTink,
			keyAlgorithm:       compositemldsa.ECDSAP256,
			classicalParams:    mustNewECDSAParameters(t, ecdsa.NistP256, ecdsa.SHA256),
		},
		{
			name:               "ECDSAP256 params with Ed25519 key",
			classicalAlgorithm: compositemldsa.ECDSAP256,
			instance:           compositemldsa.MLDSA65,
			variant:            compositemldsa.VariantTink,
			keyAlgorithm:       compositemldsa.Ed25519,
			classicalParams:    mustNewED25519Parameters(t),
		},
		{
			name:               "ECDSAP384 params with ECDSAP256 key",
			classicalAlgorithm: compositemldsa.ECDSAP384,
			instance:           compositemldsa.MLDSA65,
			variant:            compositemldsa.VariantTink,
			keyAlgorithm:       compositemldsa.ECDSAP256,
			classicalParams:    mustNewECDSAParameters(t, ecdsa.NistP256, ecdsa.SHA256),
		},
		{
			name:               "RSA3072PSS params with RSA3072PKCS1 key",
			classicalAlgorithm: compositemldsa.RSA3072PSS,
			instance:           compositemldsa.MLDSA65,
			variant:            compositemldsa.VariantTink,
			keyAlgorithm:       compositemldsa.RSA3072PKCS1,
			classicalParams:    mustNewRSAPKCS1Parameters(t, 3072, rsassapkcs1.SHA256),
		},
		{
			name:               "RSA3072PKCS1 params with RSA3072PSS key",
			classicalAlgorithm: compositemldsa.RSA3072PKCS1,
			instance:           compositemldsa.MLDSA65,
			variant:            compositemldsa.VariantTink,
			keyAlgorithm:       compositemldsa.RSA3072PSS,
			classicalParams:    mustNewRSAPSSParameters(t, 3072, rsassapss.SHA256, rsassapss.SHA256, 32),
		},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			params, err := compositemldsa.NewParameters(tc.classicalAlgorithm, tc.instance, tc.variant)
			if err != nil {
				t.Fatalf("compositemldsa.NewParameters(%v, %v, %v) err = %v, want nil", tc.classicalAlgorithm, tc.instance, tc.variant, err)
			}
			_, mlDSAPubKey := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(tc.instance))
			_, classicalPubKey := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(tc.keyAlgorithm), tc.classicalParams)
			keyID := uint32(0x12345678)
			_, err = compositemldsa.NewPublicKey(mlDSAPubKey, classicalPubKey, keyID, params)
			if err == nil {
				t.Errorf("compositemldsa.NewPublicKey(%v, %v, %v, %v) err = nil, want error", mlDSAPubKey, classicalPubKey, keyID, params)
			}
		})
	}
}

func TestNewPublicKeyRSAPSSInvalidPublicExponent(t *testing.T) {
	params, err := compositemldsa.NewParameters(compositemldsa.RSA3072PSS, compositemldsa.MLDSA65, compositemldsa.VariantTink)
	if err != nil {
		t.Fatalf("compositemldsa.NewParameters(compositemldsa.RSA3072PSS, compositemldsa.MLDSA65, compositemldsa.VariantTink) err = %v, want nil", err)
	}
	_, mlDsaPubKey := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(compositemldsa.MLDSA65))
	classicalPriv, _ := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(compositemldsa.RSA3072PSS), mustNewRSAPSSParameters(t, 3072, rsassapss.SHA256, rsassapss.SHA256, 32))

	// Create a new classical public key with an invalid public exponent.
	classicalPubKeyHandle, err := classicalPriv.(*rsassapss.PrivateKey).PublicKey()
	if err != nil {
		t.Fatalf("classicalPriv.PublicKey() err = %v, want nil", err)
	}
	classicalPubKey := classicalPubKeyHandle.(*rsassapss.PublicKey)
	classicalParams := classicalPubKey.Parameters().(*rsassapss.Parameters)
	newClassicalParams, err := rsassapss.NewParameters(rsassapss.ParametersValues{
		ModulusSizeBits: classicalParams.ModulusSizeBits(),
		SigHashType:     classicalParams.SigHashType(),
		MGF1HashType:    classicalParams.MGF1HashType(),
		PublicExponent:  f4 + 2, // Invalid public exponent.
		SaltLengthBytes: classicalParams.SaltLengthBytes(),
	}, classicalParams.Variant())
	if err != nil {
		t.Fatalf("rsassapss.NewParameters err = %v, want nil", err)
	}
	newClassicalPubKey, err := rsassapss.NewPublicKey(classicalPubKey.Modulus(), 0, newClassicalParams)
	if err != nil {
		t.Fatalf("rsassapss.NewPublicKey err = %v, want nil", err)
	}

	keyID := uint32(0x12345678)
	_, err = compositemldsa.NewPublicKey(mlDsaPubKey, newClassicalPubKey, keyID, params)
	if err == nil {
		t.Errorf("compositemldsa.NewPublicKey(%v, %v, %v, %v) err = nil, want error", mlDsaPubKey, newClassicalPubKey, keyID, params)
	}
}

func TestNewPrivateKeySuccess(t *testing.T) {
	for _, tc := range compmldsatestvectors.TestCasesSupportedParameters(t) {
		classicalAlgorithm := compositemldsa.ClassicalAlgorithm(tc.ClassicalAlgorithm)
		instance := compositemldsa.MLDSAInstance(tc.Instance)
		variant := compositemldsa.Variant(tc.Variant)
		testName := fmt.Sprintf("%v-%v-%v", classicalAlgorithm, instance, variant)
		t.Run(testName, func(t *testing.T) {
			params, err := compositemldsa.NewParameters(classicalAlgorithm, instance, variant)
			if err != nil {
				t.Fatalf("compositemldsa.NewParameters(%v, %v, %v) err = %v, want nil", classicalAlgorithm, instance, variant, err)
			}
			mldsaPrivKey, _ := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(instance))
			classicalPrivKey, _ := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(classicalAlgorithm), tc.ClassicalParams)
			keyID := uint32(0x12345678)
			privKey, err := compositemldsa.NewPrivateKey(mldsaPrivKey, classicalPrivKey, keyID, params)
			if err != nil {
				t.Fatalf("compositemldsa.NewPrivateKey(%v, %v, %v, %v) err = %v, want nil", mldsaPrivKey, classicalPrivKey, keyID, params, err)
			}
			if !privKey.Parameters().Equal(params) {
				t.Errorf("%v, %v, %v: privKey.Parameters() = %v, want %v", tc.ClassicalAlgorithm, tc.Instance, tc.Variant, privKey.Parameters(), params)
			}
			if gotID, gotOK := privKey.IDRequirement(); gotOK != params.HasIDRequirement() || (gotOK && gotID != keyID) {
				t.Errorf("privKey.IDRequirement() = %v, %v, want %v, %v", gotID, gotOK, keyID, params.HasIDRequirement())
			}
			var expectedOutputPrefix []byte
			if params.Variant() == compositemldsa.VariantTink {
				expectedOutputPrefix = outputprefix.Tink(keyID)
			}
			if !bytes.Equal(privKey.OutputPrefix(), expectedOutputPrefix) {
				t.Errorf("privKey.OutputPrefix() = %v, want %v", privKey.OutputPrefix(), expectedOutputPrefix)
			}
			if !privKey.MLDSAPrivateKey().Equal(mldsaPrivKey) {
				t.Errorf("privKey.MLDSAPrivateKey() = %v, want %v", privKey.MLDSAPrivateKey(), mldsaPrivKey)
			}
			if !privKey.ClassicalPrivateKey().Equal(classicalPrivKey) {
				t.Errorf("privKey.ClassicalPrivateKey() = %v, want %v", privKey.ClassicalPrivateKey(), classicalPrivKey)
			}
		})
	}
}

func TestPrivateKeyEqual(t *testing.T) {
	for _, tc := range compmldsatestvectors.TestCasesSupportedParameters(t) {
		classicalAlgorithm := compositemldsa.ClassicalAlgorithm(tc.ClassicalAlgorithm)
		instance := compositemldsa.MLDSAInstance(tc.Instance)
		variant := compositemldsa.Variant(tc.Variant)
		testName := fmt.Sprintf("%v-%v-%v", classicalAlgorithm, instance, variant)
		t.Run(testName, func(t *testing.T) {
			params, err := compositemldsa.NewParameters(classicalAlgorithm, instance, variant)
			if err != nil {
				t.Fatalf("compositemldsa.NewParameters(%v, %v, %v) err = %v, want nil", classicalAlgorithm, instance, variant, err)
			}
			mldsaPrivKey, _ := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(instance))
			classicalPrivKey, _ := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(classicalAlgorithm), tc.ClassicalParams)
			keyID := uint32(0x12345678)
			privKey, err := compositemldsa.NewPrivateKey(mldsaPrivKey, classicalPrivKey, keyID, params)
			if err != nil {
				t.Fatalf("compositemldsa.NewPrivateKey(%v, %v, %v, %v) err = %v, want nil", mldsaPrivKey, classicalPrivKey, keyID, params, err)
			}
			if !privKey.Equal(privKey) {
				t.Errorf("privKey.Equal(privKey) = false, want true")
			}

			// Create a second key with the same parameters.
			mldsaPrivKey2, _ := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(instance))
			classicalPrivKey2, _ := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(classicalAlgorithm), tc.ClassicalParams)
			privKey2, err := compositemldsa.NewPrivateKey(mldsaPrivKey2, classicalPrivKey2, keyID, params)
			if err != nil {
				t.Fatalf("compositemldsa.NewPrivateKey(%v, %v, %v, %v) err = %v, want nil", mldsaPrivKey2, classicalPrivKey2, keyID, params, err)
			}
			if privKey.Equal(privKey2) {
				t.Errorf("privKey.Equal(privKey2) = true, want false")
			}
		})
	}
}

func TestNewPrivateKeyFailsOnMistypedClassicalKey(t *testing.T) {
	for _, tc := range compmldsatestvectors.TestCasesSupportedParameters(t) {
		classicalAlgorithm := compositemldsa.ClassicalAlgorithm(tc.ClassicalAlgorithm)
		instance := compositemldsa.MLDSAInstance(tc.Instance)
		variant := compositemldsa.Variant(tc.Variant)
		testName := fmt.Sprintf("%v-%v-%v", classicalAlgorithm, instance, variant)
		t.Run(testName, func(t *testing.T) {
			params, err := compositemldsa.NewParameters(classicalAlgorithm, instance, variant)
			if err != nil {
				t.Fatalf("compositemldsa.NewParameters(%v, %v, %v) err = %v, want nil", classicalAlgorithm, instance, variant, err)
			}
			mldsaPrivKey, _ := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(instance))
			_, classicalPubKey := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(classicalAlgorithm), tc.ClassicalParams)
			const keyID = uint32(0x12345678)
			if _, err = compositemldsa.NewPrivateKey(mldsaPrivKey, classicalPubKey, keyID, params); err == nil {
				t.Errorf("compositemldsa.NewPrivateKey(%v, %v, %v, %v) err = nil, want error", mldsaPrivKey, classicalPubKey, keyID, params)
			}
		})
	}
}

func TestPrivateKeyNotEqual(t *testing.T) {
	params, err := compositemldsa.NewParameters(compositemldsa.Ed25519, compositemldsa.MLDSA65, compositemldsa.VariantTink)
	if err != nil {
		t.Fatalf("compositemldsa.NewParameters(Ed25519, MLDSA65, VariantTink) err = %v, want nil", err)
	}
	keyID := uint32(0x12345678)
	classicalParams := mustNewED25519Parameters(t)

	// Baseline key
	mldsaPrivKey1, _ := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(compositemldsa.MLDSA65))
	classicalPrivKey1, _ := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(compositemldsa.Ed25519), classicalParams)
	privKey1, err := compositemldsa.NewPrivateKey(mldsaPrivKey1, classicalPrivKey1, keyID, params)
	if err != nil {
		t.Fatalf("compositemldsa.NewPrivateKey() err = %v, want nil", err)
	}

	tests := []struct {
		name    string
		other   key.Key
		wantErr bool
	}{
		{
			name: "Different PublicKey (keyID)",
			other: func() key.Key {
				otherKeyID := uint32(0x87654321)
				privKeyOtherID, err := compositemldsa.NewPrivateKey(mldsaPrivKey1, classicalPrivKey1, otherKeyID, params)
				if err != nil {
					t.Fatalf("compositemldsa.NewPrivateKey() err = %v, want nil", err)
				}
				return privKeyOtherID
			}(),
		},
		{
			name: "Different ML-DSA Private Key",
			other: func() key.Key {
				mldsaPrivKey2, _ := compmldsatestvectors.GenerateMLDSAKeyPair(t, internalcompmldsa.MLDSAInstance(compositemldsa.MLDSA65))
				privKeyDiffMLDSA, err := compositemldsa.NewPrivateKey(mldsaPrivKey2, classicalPrivKey1, keyID, params)
				if err != nil {
					t.Fatalf("compositemldsa.NewPrivateKey() err = %v, want nil", err)
				}
				return privKeyDiffMLDSA
			}(),
		},
		{
			name: "Different Classical Private Key",
			other: func() key.Key {
				classicalPrivKey2, _ := compmldsatestvectors.GenerateClassicalKeyPair(t, internalcompmldsa.ClassicalAlgorithm(compositemldsa.Ed25519), classicalParams)
				privKeyDiffClassical, err := compositemldsa.NewPrivateKey(mldsaPrivKey1, classicalPrivKey2, keyID, params)
				if err != nil {
					t.Fatalf("compositemldsa.NewPrivateKey() err = %v, want nil", err)
				}
				return privKeyDiffClassical
			}(),
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			if privKey1.Equal(test.other) {
				t.Errorf("privKey1.Equal(%s) = true, want false", test.name)
			}
		})
	}
}

func mustNewED25519Parameters(t *testing.T) key.Parameters {
	t.Helper()
	params, err := ed25519.NewParameters(ed25519.VariantNoPrefix)
	if err != nil {
		t.Fatalf("ed25519.NewParameters(ed25519.VariantNoPrefix) err = %v, want nil", err)
	}
	return &params
}

func mustNewECDSAParameters(t *testing.T, curve ecdsa.CurveType, hashType ecdsa.HashType) key.Parameters {
	t.Helper()
	params, err := ecdsa.NewParameters(curve, hashType, ecdsa.DER, ecdsa.VariantNoPrefix)
	if err != nil {
		t.Fatalf("ecdsa.NewParameters(%v, %v, ecdsa.DER, ecdsa.VariantNoPrefix) err = %v, want nil", curve, hashType, err)
	}
	return params
}

func mustNewRSAPSSParameters(t *testing.T, modulusSizeBits int, sigHashType rsassapss.HashType, mgf1HashType rsassapss.HashType, saltLengthBytes int) key.Parameters {
	t.Helper()
	params, err := rsassapss.NewParameters(rsassapss.ParametersValues{
		ModulusSizeBits: modulusSizeBits,
		SigHashType:     sigHashType,
		MGF1HashType:    mgf1HashType,
		PublicExponent:  f4,
		SaltLengthBytes: saltLengthBytes,
	}, rsassapss.VariantNoPrefix)
	if err != nil {
		t.Fatalf("rsassapss.NewParameters err = %v, want nil", err)
	}
	return params
}

func mustNewRSAPKCS1Parameters(t *testing.T, modulusSizeBits int, hashType rsassapkcs1.HashType) key.Parameters {
	t.Helper()
	params, err := rsassapkcs1.NewParameters(modulusSizeBits, hashType, f4, rsassapkcs1.VariantNoPrefix)
	if err != nil {
		t.Fatalf("rsassapkcs1.NewParameters err = %v, want nil", err)
	}
	return params
}
