// Copyright 2021 Google Inc.
//
// 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.

//go:build (!localtest || !tpm12) && cgo && !gofuzz
// +build !localtest !tpm12
// +build cgo
// +build !gofuzz

// NOTE: simulator requires cgo, hence the build tag.

package attest

import (
	"bytes"
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/asn1"
	"math/big"
	"testing"
)

func TestSimTPM20KeyCreateAndLoad(t *testing.T) {
	sim, tpm := setupSimulatedTPM(t)
	defer sim.Close()
	testKeyCreateAndLoad(t, tpm)
}

func TestTPM20KeyCreateAndLoad(t *testing.T) {
	if !*testLocal {
		t.SkipNow()
	}
	tpm, err := OpenTPM(nil)
	if err != nil {
		t.Fatalf("OpenTPM() failed: %v", err)
	}
	defer tpm.Close()
	testKeyCreateAndLoad(t, tpm)
}

func testKeyCreateAndLoad(t *testing.T, tpm *TPM) {
	ak, err := tpm.NewAK(nil)
	if err != nil {
		t.Fatalf("NewAK() failed: %v", err)
	}
	for _, test := range []struct {
		name string
		opts *KeyConfig
	}{
		{
			name: "default",
			opts: nil,
		},
		{
			name: "ECDSAP256-SHA256",
			opts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      256,
			},
		},
		{
			name: "ECDSAP384-SHA384",
			opts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      384,
			},
		},
		{
			name: "ECDSAP521-SHA512",
			opts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      521,
			},
		},
		{
			name: "RSA-1024",
			opts: &KeyConfig{
				Algorithm: RSA,
				Size:      1024,
			},
		},
		{
			name: "RSA-2048",
			opts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			sk, err := tpm.NewKey(ak, test.opts)
			if err != nil {
				t.Fatalf("NewKey() failed: %v", err)
			}
			defer sk.Close()

			enc, err := sk.Marshal()
			if err != nil {
				t.Fatalf("sk.Marshal() failed: %v", err)
			}
			if err := sk.Close(); err != nil {
				t.Fatalf("sk.Close() failed: %v", err)
			}

			loaded, err := tpm.LoadKey(enc)
			if err != nil {
				t.Fatalf("LoadKey() failed: %v", err)
			}
			defer loaded.Close()

			k1, k2 := sk.key.(*wrappedKey20), loaded.key.(*wrappedKey20)
			if !bytes.Equal(k1.public, k2.public) {
				t.Error("Original & loaded Key public blobs did not match.")
				t.Logf("Original = %v", k1.public)
				t.Logf("Loaded   = %v", k2.public)
			}

			priv1, err := sk.Private(sk.Public())
			if err != nil {
				t.Fatalf("sk.Private() failed: %v", err)
			}
			signer1, ok := priv1.(crypto.Signer)
			if !ok {
				t.Fatalf("want crypto.Signer, got %T", priv1)
			}
			pk1, err := x509.MarshalPKIXPublicKey(signer1.Public())
			if err != nil {
				t.Fatalf("cannot marshal public key: %v", err)
			}

			priv2, err := loaded.Private(loaded.Public())
			if err != nil {
				t.Fatalf("loaded.Private() failed: %v", err)
			}
			signer2, ok := priv2.(crypto.Signer)
			if !ok {
				t.Fatalf("want crypto.Signer, got %T", priv2)
			}
			pk2, err := x509.MarshalPKIXPublicKey(signer2.Public())
			if err != nil {
				t.Fatalf("cannot marshal public key: %v", err)
			}

			if !bytes.Equal(pk1, pk2) {
				t.Error("public keys do not match")
			}
		})
	}
}

func TestSimTPM20KeySign(t *testing.T) {
	sim, tpm := setupSimulatedTPM(t)
	defer sim.Close()
	testKeySign(t, tpm)
}

func TestTPM20KeySign(t *testing.T) {
	if !*testLocal {
		t.SkipNow()
	}
	tpm, err := OpenTPM(nil)
	if err != nil {
		t.Fatalf("OpenTPM() failed: %v", err)
	}
	defer tpm.Close()
	testKeySign(t, tpm)
}

func testKeySign(t *testing.T, tpm *TPM) {
	ak, err := tpm.NewAK(nil)
	if err != nil {
		t.Fatalf("NewAK() failed: %v", err)
	}

	for _, test := range []struct {
		name     string
		keyOpts  *KeyConfig
		signOpts crypto.SignerOpts
		digest   []byte
	}{
		{
			name:     "default",
			keyOpts:  nil,
			signOpts: nil,
			digest:   []byte("12345678901234567890123456789012"),
		},
		{
			name: "ECDSAP256-SHA256",
			keyOpts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      256,
			},
			signOpts: nil,
			digest:   []byte("12345678901234567890123456789012"),
		},
		{
			name: "ECDSAP384-SHA384",
			keyOpts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      384,
			},
			signOpts: nil,
			digest:   []byte("123456789012345678901234567890121234567890123456"),
		},
		{
			name: "ECDSAP521-SHA512",
			keyOpts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      521,
			},
			signOpts: nil,
			digest:   []byte("1234567890123456789012345678901212345678901234567890123456789012"),
		},
		{
			name: "RSA2048-PKCS1v15-SHA256",
			keyOpts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			signOpts: crypto.SHA256,
			digest:   []byte("12345678901234567890123456789012"),
		},
		{
			name: "RSA2048-PKCS1v15-SHA384",
			keyOpts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			signOpts: crypto.SHA384,
			digest:   []byte("123456789012345678901234567890121234567890123456"),
		},
		{
			name: "RSA2048-PKCS1v15-SHA512",
			keyOpts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			signOpts: crypto.SHA512,
			digest:   []byte("1234567890123456789012345678901212345678901234567890123456789012"),
		},
		{
			name: "RSA2048-PSS-SHA256",
			keyOpts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			signOpts: &rsa.PSSOptions{
				SaltLength: rsa.PSSSaltLengthAuto,
				Hash:       crypto.SHA256,
			},
			digest: []byte("12345678901234567890123456789012"),
		},
		{
			name: "RSA2048-PSS-SHA384",
			keyOpts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			signOpts: &rsa.PSSOptions{
				SaltLength: rsa.PSSSaltLengthAuto,
				Hash:       crypto.SHA384,
			},
			digest: []byte("123456789012345678901234567890121234567890123456"),
		},
		{
			name: "RSA2048-PSS-SHA512",
			keyOpts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			signOpts: &rsa.PSSOptions{
				SaltLength: rsa.PSSSaltLengthAuto,
				Hash:       crypto.SHA512,
			},
			digest: []byte("1234567890123456789012345678901212345678901234567890123456789012"),
		},
		{
			name: "RSA2048-PSS-SHA256, explicit salt len",
			keyOpts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			signOpts: &rsa.PSSOptions{
				SaltLength: 32,
				Hash:       crypto.SHA256,
			},
			digest: []byte("12345678901234567890123456789012"),
		},
		{
			name: "RSA2048-PSS-SHA384, explicit salt len",
			keyOpts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			signOpts: &rsa.PSSOptions{
				SaltLength: 48,
				Hash:       crypto.SHA384,
			},
			digest: []byte("123456789012345678901234567890121234567890123456"),
		},
		{
			name: "RSA2048-PSS-SHA512, explicit salt len",
			keyOpts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			signOpts: &rsa.PSSOptions{
				SaltLength: 64,
				Hash:       crypto.SHA512,
			},
			digest: []byte("1234567890123456789012345678901212345678901234567890123456789012"),
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			sk, err := tpm.NewKey(ak, test.keyOpts)
			if err != nil {
				t.Fatalf("NewKey() failed: %v", err)
			}
			defer sk.Close()

			pub := sk.Public()
			priv, err := sk.Private(pub)
			if err != nil {
				t.Fatalf("sk.Private() failed: %v", err)
			}
			signer, ok := priv.(crypto.Signer)
			if !ok {
				t.Fatalf("want crypto.Signer, got %T", priv)
			}
			sig, err := signer.Sign(rand.Reader, test.digest, test.signOpts)
			if err != nil {
				t.Fatalf("signer.Sign() failed: %v", err)
			}

			if test.keyOpts == nil || test.keyOpts.Algorithm == ECDSA {
				verifyECDSA(t, pub, test.digest, sig)
			} else {
				verifyRSA(t, pub, test.digest, sig, test.signOpts)
			}
		})
	}
}

func verifyECDSA(t *testing.T, pub crypto.PublicKey, digest, sig []byte) {
	t.Helper()
	parsed := struct{ R, S *big.Int }{}
	_, err := asn1.Unmarshal(sig, &parsed)
	if err != nil {
		t.Fatalf("signature parsing failed: %v", err)
	}
	pubECDSA, ok := pub.(*ecdsa.PublicKey)
	if !ok {
		t.Fatalf("want *ecdsa.PublicKey, got %T", pub)
	}
	if !ecdsa.Verify(pubECDSA, digest[:], parsed.R, parsed.S) {
		t.Fatalf("ecdsa.Verify() failed")
	}
}

func verifyRSA(t *testing.T, pub crypto.PublicKey, digest, sig []byte, opts crypto.SignerOpts) {
	t.Helper()
	pubRSA, ok := pub.(*rsa.PublicKey)
	if !ok {
		t.Fatalf("want *rsa.PublicKey, got %T", pub)
	}
	if pss, ok := opts.(*rsa.PSSOptions); ok {
		if err := rsa.VerifyPSS(pubRSA, opts.HashFunc(), digest, sig, pss); err != nil {
			t.Fatalf("rsa.VerifyPSS(): %v", err)
		}
	} else {
		if err := rsa.VerifyPKCS1v15(pubRSA, opts.HashFunc(), digest, sig); err != nil {
			t.Fatalf("signature verification failed: %v", err)
		}
	}
}

func TestSimTPM20KeyOpts(t *testing.T) {
	sim, tpm := setupSimulatedTPM(t)
	defer sim.Close()
	testKeyOpts(t, tpm)
}

func TestTPM20KeyOpts(t *testing.T) {
	if !*testLocal {
		t.SkipNow()
	}
	tpm, err := OpenTPM(nil)
	if err != nil {
		t.Fatalf("OpenTPM() failed: %v", err)
	}
	defer tpm.Close()
	testKeyOpts(t, tpm)
}

func testKeyOpts(t *testing.T, tpm *TPM) {
	ak, err := tpm.NewAK(nil)
	if err != nil {
		t.Fatalf("NewAK() failed: %v", err)
	}
	for _, test := range []struct {
		name string
		opts *KeyConfig
		err  bool
	}{
		{
			name: "wrong alg",
			opts: &KeyConfig{
				Algorithm: "fake alg",
			},
			err: true,
		},
		{
			name: "wrong size",
			opts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      1234,
			},
			err: true,
		},
		{
			name: "default",
			opts: nil,
			err:  false,
		},
		{
			name: "ECDSAP256",
			opts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      256,
			},
			err: false,
		},
		{
			name: "ECDSAP384",
			opts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      384,
			},
			err: false,
		},
		{
			name: "ECDSAP521",
			opts: &KeyConfig{
				Algorithm: ECDSA,
				Size:      521,
			},
			err: false,
		},
		{
			name: "RSA-1024",
			opts: &KeyConfig{
				Algorithm: RSA,
				Size:      1024,
			},
			err: false,
		},
		{
			name: "RSA-2048",
			opts: &KeyConfig{
				Algorithm: RSA,
				Size:      2048,
			},
			err: false,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			sk, err := tpm.NewKey(ak, test.opts)
			if !test.err && err != nil {
				t.Fatalf("NewKey() failed: %v", err)
			}
			if test.err {
				if err == nil {
					sk.Close()
					t.Fatalf("NewKey(): expected err != nil")
				}
				return
			}
			defer sk.Close()

			expected := test.opts
			if expected == nil {
				expected = defaultConfig
			}

			switch pub := sk.Public().(type) {
			case *ecdsa.PublicKey:
				if expected.Algorithm != ECDSA {
					t.Errorf("incorrect key type generated, expected %q, got EC", expected.Algorithm)
				}
				sizeToCurve := map[int]elliptic.Curve{
					256: elliptic.P256(),
					384: elliptic.P384(),
					521: elliptic.P521(),
				}
				expectedCurve, ok := sizeToCurve[expected.Size]
				if !ok {
					t.Fatalf("cannot match curve to key size %d", expected.Size)
				}
				if expectedCurve != pub.Curve {
					t.Errorf("incorrect curve, expected %v, got %v", expectedCurve, pub.Curve)
				}
			case *rsa.PublicKey:
				if expected.Algorithm != RSA {
					t.Errorf("incorrect key type, expected %q, got RSA", expected.Algorithm)
				}
				if pub.Size()*8 != expected.Size {
					t.Errorf("incorrect key size, expected %d, got %d", expected.Size, pub.Size()*8)
				}
			default:
				t.Errorf("unsupported key type: %T", pub)
			}
		})
	}
}
