// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1

package releaseauth

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"strings"

	"github.com/ProtonMail/go-crypto/openpgp"
	openpgpErrors "github.com/ProtonMail/go-crypto/openpgp/errors"
)

// SignatureAuthentication is an archive Authenticator that validates that SHA256SUMS data
// was signed by the given signing key.
type SignatureAuthentication struct {
	Authenticator

	// This can be overridden by tests to check arbitrary keys, rather than the HashiCorp public key
	PublicKey string
	signature []byte
	signed    []byte
}

var _ Authenticator = SignatureAuthentication{}

// ErrNotSignedByHashiCorp is the error returned when there is a mismatch between the SHA256SUMS
// signature data and the data itself.
var ErrNotSignedByHashiCorp = errors.New("failed to authenticate that the archive was signed by HashiCorp")

// NewSignatureAuthentication creates a new Authenticator given some signature data
// (the SHA256SUMS.sig file), the signed data (the SHA256SUMS file), and a public key
func NewSignatureAuthentication(signature []byte, signed []byte) *SignatureAuthentication {
	return &SignatureAuthentication{
		signature: signature,
		signed:    signed,
		PublicKey: HashiCorpPublicKey,
	}
}

func (a SignatureAuthentication) Authenticate() error {
	// Verify the signature using the HashiCorp public key. If this succeeds,
	// this is an official provider.
	hashicorpKeyring, err := openpgp.ReadArmoredKeyRing(strings.NewReader(a.PublicKey))
	if err != nil {
		return fmt.Errorf("error creating HashiCorp keyring: %s", err)
	}

	entity, err := openpgp.CheckDetachedSignature(hashicorpKeyring, bytes.NewReader(a.signed), bytes.NewReader(a.signature), nil)
	if err == openpgpErrors.ErrKeyExpired {
		for id := range entity.Identities {
			log.Printf("[WARN] expired openpgp key from %s\n", id)
		}
		err = nil
	}
	if err != nil {
		log.Printf("[DEBUG] GPG reported an error while verifying detached signature: %s", err)
		return ErrNotSignedByHashiCorp
	}

	return nil
}

// HashicorpPublicKey is the HashiCorp public key, also available at
// https://www.hashicorp.com/security
const HashiCorpPublicKeyID = "72D7468F"
const HashiCorpPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----

[encoded data, 7605 characters]
ZF5q4h4I33PSGDdSvGXn9UMY5Isjpg==
=7pIB
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----

[encoded data, 7605 characters]
GCHx/5nGBdDwoxEsXqpI3IUqPb6FDg==
=wtp+
-----END PGP PUBLIC KEY BLOCK-----`
