// Copyright 2021 The Sigstore Authors
//
// 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 resetyubikey && e2e && !pivkeydisabled
// +build resetyubikey,e2e,!pivkeydisabled

// DANGER
// This test requires a yubikey to be present. It WILL reset the yubikey to exercise functionality.
// DO NOT RUN THIS TEST IF YOU DO NOT WANT TO RESET YOUR YUBIKEY
// This requires the "resetyubikey" tag to be passed to go test.
// See https://docs.sigstore.dev/key_management/hardware-based-tokens/#tested-devices

package test

import (
	"context"
	"crypto/x509"
	"testing"

	// Import the functions directly for testing.
	. "github.com/sigstore/cosign/v2/cmd/cosign/cli/pivcli"
)

func TestSetManagementKeyCmd(t *testing.T) {
	ctx := context.Background()

	Confirm = func(_ string) bool { return true }
	must(ResetKeyCmd(ctx), t)

	// The key should be the default management key so this should fail
	mustErr(SetManagementKeyCmd(ctx, "foobar", "newkey", false), t)
	must(SetManagementKeyCmd(ctx, "", "newkey", false), t)

	// Now it should fail with the wrong key
	mustErr(SetManagementKeyCmd(ctx, "", "otherkey", false), t)
	// But pass if we use the right key
	must(SetManagementKeyCmd(ctx, "newkey", "otherkey", false), t)

	// Reset and try a random key too!
	must(ResetKeyCmd(ctx), t)
	must(SetManagementKeyCmd(ctx, "", "", true), t)
	mustErr(SetManagementKeyCmd(ctx, "", "", true), t)
}

func TestSetPUKCmd(t *testing.T) {
	ctx := context.Background()

	Confirm = func(_ string) bool { return true }
	must(ResetKeyCmd(ctx), t)

	// The PUK should be the default key so this should fail
	mustErr(SetPukCmd(ctx, "11111111", "12121212"), t)
	must(SetPukCmd(ctx, "", "12121212"), t)

	// Now it should fail with the wrong key
	mustErr(SetPukCmd(ctx, "", "43214321"), t)
	// But pass if we use the right key
	must(SetPukCmd(ctx, "12121212", "43214321"), t)
}

func TestSetPinCmd(t *testing.T) {
	ctx := context.Background()

	Confirm = func(_ string) bool { return true }
	must(ResetKeyCmd(ctx), t)

	// The PIN should be the default PIN so this should fail
	mustErr(SetPinCmd(ctx, "111111", "222222"), t)
	must(SetPinCmd(ctx, "", "222222"), t)

	// Now it should fail with the wrong key
	mustErr(SetPinCmd(ctx, "333333", "444444"), t)
	// But pass if we use the right key
	must(SetPinCmd(ctx, "222222", "111111"), t)
}

func TestUnblockCmd(t *testing.T) {
	ctx := context.Background()

	Confirm = func(_ string) bool { return true }
	must(ResetKeyCmd(ctx), t)

	// Set a PUK
	must(SetPukCmd(ctx, "", "43214321"), t)
	// Set the pin to something, then lock the device by trying the wrong one too many times.
	must(SetPinCmd(ctx, "", "111111"), t)

	for i := 0; i < 5; i++ {
		mustErr(SetPinCmd(ctx, "222222", "333333"), t)
	}

	// Now even with the right PIN it should be stuck
	mustErr(SetPinCmd(ctx, "111111", "222222"), t)

	// But we can unblock it
	must(UnblockCmd(ctx, "43214321", "222222"), t)
	must(SetPinCmd(ctx, "222222", "333333"), t)
}

func TestGenerateKeyCmd(t *testing.T) {
	ctx := context.Background()

	Confirm = func(_ string) bool { return true }
	must(ResetKeyCmd(ctx), t)

	// This should work with the default key
	must(GenerateKeyCmd(ctx, "", false, "", "", ""), t)

	// Set the key to something other than the default
	must(SetManagementKeyCmd(ctx, "", "mynewkey", false), t)
	// Now this should fail
	mustErr(GenerateKeyCmd(ctx, "", false, "", "", ""), t)
	// Unless we use the right key
	must(GenerateKeyCmd(ctx, "mynewkey", false, "", "", ""), t)

	// Now if we use a random key it should set a new one
	must(GenerateKeyCmd(ctx, "mynewkey", true, "", "", ""), t)
	// The old one shouldn't work.
	mustErr(GenerateKeyCmd(ctx, "mynewkey", false, "", "", ""), t)
}

func TestAttestationCmd(t *testing.T) {
	ctx := context.Background()

	Confirm = func(_ string) bool { return true }
	must(ResetKeyCmd(ctx), t)
	must(GenerateKeyCmd(ctx, "", false, "", "", ""), t)

	attestations, err := AttestationCmd(ctx, "")
	if err != nil {
		t.Fatal(err)
	}

	root := x509.NewCertPool()
	if !root.AppendCertsFromPEM([]byte(yubicoCert)) {
		t.Fatal("error adding roots")
	}

	// Check the device against the manufacturer
	if _, err := attestations.DeviceCert.Verify(x509.VerifyOptions{
		Roots: root,
	}); err != nil {
		t.Fatal(err)
	}

	intermediate := x509.NewCertPool()
	intermediate.AddCert(attestations.DeviceCert)
	// Now check the key, with the device as a chain
	if _, err := attestations.KeyCert.Verify(x509.VerifyOptions{
		Roots:         root,
		Intermediates: intermediate,
	}); err != nil {
		// This is known to fail on YubiKey firmware 4.3
		// See https://labanskoller.se/blog/2019/12/30/pki-is-hard-how-yubico-trusted-openssl-and-got-it-wrong/
		//
		if attestations.KeyAttestation.Version.Major == 4 &&
			attestations.KeyAttestation.Version.Minor == 3 {
			t.Skipf("key attestation cert chain verification is known to be broken on firmware 4.3")
		} else {
			t.Fatal(err)
		}

	}

}

const yubicoCert = `-----BEGIN CERTIFICATE-----
MIIDFzCCAf+gAwIBAgIDBAZHMA0GCSqGSIb3DQEBCwUAMCsxKTAnBgNVBAMMIFl1
YmljbyBQSVYgUm9vdCBDQSBTZXJpYWwgMjYzNzUxMCAXDTE2MDMxNDAwMDAwMFoY
DzIwNTIwNDE3MDAwMDAwWjArMSkwJwYDVQQDDCBZdWJpY28gUElWIFJvb3QgQ0Eg
U2VyaWFsIDI2Mzc1MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMN2
cMTNR6YCdcTFRxuPy31PabRn5m6pJ+nSE0HRWpoaM8fc8wHC+Tmb98jmNvhWNE2E
ilU85uYKfEFP9d6Q2GmytqBnxZsAa3KqZiCCx2LwQ4iYEOb1llgotVr/whEpdVOq
joU0P5e1j1y7OfwOvky/+AXIN/9Xp0VFlYRk2tQ9GcdYKDmqU+db9iKwpAzid4oH
BVLIhmD3pvkWaRA2H3DA9t7H/HNq5v3OiO1jyLZeKqZoMbPObrxqDg+9fOdShzgf
wCqgT3XVmTeiwvBSTctyi9mHQfYd2DwkaqxRnLbNVyK9zl+DzjSGp9IhVPiVtGet
X02dxhQnGS7K6BO0Qe8CAwEAAaNCMEAwHQYDVR0OBBYEFMpfyvLEojGc6SJf8ez0
1d8Cv4O/MA8GA1UdEwQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
DQEBCwUAA4IBAQBc7Ih8Bc1fkC+FyN1fhjWioBCMr3vjneh7MLbA6kSoyWF70N3s
XhbXvT4eRh0hvxqvMZNjPU/VlRn6gLVtoEikDLrYFXN6Hh6Wmyy1GTnspnOvMvz2
lLKuym9KYdYLDgnj3BeAvzIhVzzYSeU77/Cupofj093OuAswW0jYvXsGTyix6B3d
bW5yWvyS9zNXaqGaUmP3U9/b6DlHdDogMLu3VLpBB9bm5bjaKWWJYgWltCVgUbFq
Fqyi4+JE014cSgR57Jcu3dZiehB6UtAPgad9L5cNvua/IWRmm+ANy3O2LH++Pyl8
SREzU8onbBsjMg9QDiSf5oJLKvd/Ren+zGY7
-----END CERTIFICATE-----`
