package authn

import (
	"crypto/ed25519"
	"encoding/base64"
	"errors"
)

var (
	// ErrInvalidClientPublicKey reports that cached client public key material
	// is not a base64-encoded raw Ed25519 public key.
	ErrInvalidClientPublicKey = errors.New("client_public_key is not a valid base64-encoded Ed25519 public key")

	// ErrInvalidRequestSignature reports that a request signature is not a raw
	// Ed25519 signature for the canonical request signing input.
	ErrInvalidRequestSignature = errors.New("invalid request signature")
)

// VerifyRequestSignature validates the base64-encoded raw Ed25519 public key
// from session cache, builds the canonical v1 signing input from fields, and
// verifies that signature authenticates the request.
func VerifyRequestSignature(clientPublicKey string, signature []byte, fields RequestSigningFields) error {
	publicKey, err := decodeClientPublicKey(clientPublicKey)
	if err != nil {
		return err
	}
	if len(signature) != ed25519.SignatureSize {
		return ErrInvalidRequestSignature
	}
	if !ed25519.Verify(publicKey, BuildRequestSigningInput(fields), signature) {
		return ErrInvalidRequestSignature
	}

	return nil
}

func decodeClientPublicKey(value string) (ed25519.PublicKey, error) {
	decoded, err := base64.StdEncoding.Strict().DecodeString(value)
	if err != nil {
		return nil, ErrInvalidClientPublicKey
	}
	if len(decoded) != ed25519.PublicKeySize {
		return nil, ErrInvalidClientPublicKey
	}

	return ed25519.PublicKey(decoded), nil
}
