// Copyright 2017 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 tls

import (
	"crypto"
	"crypto/ed25519"
	"testing"
)

func TestSignatureSelection(t *testing.T) {
	rsaCert := &testRSAPrivateKey.PublicKey
	ecdsaCert := &testECDSAPrivateKey.PublicKey
	ed25519Cert := testEd25519PrivateKey.Public().(ed25519.PublicKey)
	sigsPKCS1WithSHA := []SignatureScheme{PKCS1WithSHA256, PKCS1WithSHA1}
	sigsPSSWithSHA := []SignatureScheme{PSSWithSHA256, PSSWithSHA384}
	sigsECDSAWithSHA := []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithSHA1}

	tests := []struct {
		pubkey      crypto.PublicKey
		peerSigAlgs []SignatureScheme
		ourSigAlgs  []SignatureScheme
		tlsVersion  uint16

		expectedSigAlg  SignatureScheme // if tlsVersion == VersionTLS12
		expectedSigType uint8
		expectedHash    crypto.Hash
	}{
		// Hash is fixed for RSA in TLS 1.1 and before.
		// https://tools.ietf.org/html/rfc4346#page-44
		{rsaCert, nil, nil, VersionTLS11, 0, signaturePKCS1v15, crypto.MD5SHA1},
		{rsaCert, nil, nil, VersionTLS10, 0, signaturePKCS1v15, crypto.MD5SHA1},
		{rsaCert, nil, nil, VersionSSL30, 0, signaturePKCS1v15, crypto.MD5SHA1},

		// Before TLS 1.2, there is no signature_algorithms extension
		// nor field in CertificateRequest and digitally-signed and thus
		// it should be ignored.
		{rsaCert, sigsPKCS1WithSHA, nil, VersionTLS11, 0, signaturePKCS1v15, crypto.MD5SHA1},
		{rsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS11, 0, signaturePKCS1v15, crypto.MD5SHA1},
		// Use SHA-1 for TLS 1.0 and 1.1 with ECDSA, see https://tools.ietf.org/html/rfc4492#page-20
		{ecdsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS11, 0, signatureECDSA, crypto.SHA1},
		{ecdsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS10, 0, signatureECDSA, crypto.SHA1},

		// TLS 1.2 without signature_algorithms extension
		// https://tools.ietf.org/html/rfc5246#page-47
		{rsaCert, nil, sigsPKCS1WithSHA, VersionTLS12, PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1},
		{ecdsaCert, nil, sigsPKCS1WithSHA, VersionTLS12, ECDSAWithSHA1, signatureECDSA, crypto.SHA1},

		{rsaCert, []SignatureScheme{PKCS1WithSHA1}, sigsPKCS1WithSHA, VersionTLS12, PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1},
		{rsaCert, []SignatureScheme{PKCS1WithSHA256}, sigsPKCS1WithSHA, VersionTLS12, PKCS1WithSHA256, signaturePKCS1v15, crypto.SHA256},
		// "sha_hash" may denote hashes other than SHA-1
		// https://tools.ietf.org/html/draft-ietf-tls-rfc4492bis-17#page-17
		{ecdsaCert, []SignatureScheme{ECDSAWithSHA1}, sigsECDSAWithSHA, VersionTLS12, ECDSAWithSHA1, signatureECDSA, crypto.SHA1},
		{ecdsaCert, []SignatureScheme{ECDSAWithP256AndSHA256}, sigsECDSAWithSHA, VersionTLS12, ECDSAWithP256AndSHA256, signatureECDSA, crypto.SHA256},

		// RSASSA-PSS is defined in TLS 1.3 for TLS 1.2
		// https://tools.ietf.org/html/draft-ietf-tls-tls13-21#page-45
		{rsaCert, []SignatureScheme{PSSWithSHA256}, sigsPSSWithSHA, VersionTLS12, PSSWithSHA256, signatureRSAPSS, crypto.SHA256},

		// All results are fixed for Ed25519. RFC 8422, Section 5.10.
		{ed25519Cert, []SignatureScheme{Ed25519}, []SignatureScheme{ECDSAWithSHA1, Ed25519}, VersionTLS12, Ed25519, signatureEd25519, directSigning},
		{ed25519Cert, nil, nil, VersionTLS12, Ed25519, signatureEd25519, directSigning},
	}

	for testNo, test := range tests {
		sigAlg, sigType, hashFunc, err := pickSignatureAlgorithm(test.pubkey, test.peerSigAlgs, test.ourSigAlgs, test.tlsVersion)
		if err != nil {
			t.Errorf("test[%d]: unexpected error: %v", testNo, err)
		}
		if test.tlsVersion == VersionTLS12 && test.expectedSigAlg != sigAlg {
			t.Errorf("test[%d]: expected signature scheme %#x, got %#x", testNo, test.expectedSigAlg, sigAlg)
		}
		if test.expectedSigType != sigType {
			t.Errorf("test[%d]: expected signature algorithm %#x, got %#x", testNo, test.expectedSigType, sigType)
		}
		if test.expectedHash != hashFunc {
			t.Errorf("test[%d]: expected hash function %#x, got %#x", testNo, test.expectedHash, hashFunc)
		}
	}

	badTests := []struct {
		pubkey      crypto.PublicKey
		peerSigAlgs []SignatureScheme
		ourSigAlgs  []SignatureScheme
		tlsVersion  uint16
	}{
		{rsaCert, sigsECDSAWithSHA, sigsPKCS1WithSHA, VersionTLS12},
		{ecdsaCert, sigsPKCS1WithSHA, sigsPKCS1WithSHA, VersionTLS12},
		{ecdsaCert, sigsECDSAWithSHA, sigsPKCS1WithSHA, VersionTLS12},
		{rsaCert, []SignatureScheme{0}, sigsPKCS1WithSHA, VersionTLS12},
		{ed25519Cert, sigsECDSAWithSHA, sigsECDSAWithSHA, VersionTLS12},
		{ed25519Cert, []SignatureScheme{Ed25519}, sigsECDSAWithSHA, VersionTLS12},
		{ecdsaCert, []SignatureScheme{Ed25519}, []SignatureScheme{Ed25519}, VersionTLS12},
		{ed25519Cert, nil, nil, VersionTLS11},
		{ed25519Cert, nil, nil, VersionTLS10},
		{ed25519Cert, nil, nil, VersionSSL30},

		// ECDSA is unspecified for SSL 3.0 in RFC 4492.
		// TODO a SSL 3.0 client cannot advertise signature_algorithms,
		// but if an application feeds an ECDSA certificate anyway, it
		// will be accepted rather than trigger a handshake failure. Ok?
		//{ecdsaCert, nil, nil, VersionSSL30},
	}

	for testNo, test := range badTests {
		sigAlg, sigType, hashFunc, err := pickSignatureAlgorithm(test.pubkey, test.peerSigAlgs, test.ourSigAlgs, test.tlsVersion)
		if err == nil {
			t.Errorf("test[%d]: unexpected success, got %#x %#x %#x", testNo, sigAlg, sigType, hashFunc)
		}
	}
}
