//go:generate ../tools/cmd/genjws.sh

// Package jws implements the digital signature on JSON based data
// structures as described in https://tools.ietf.org/html/rfc7515
//
// If you do not care about the details, the only things that you
// would need to use are the following functions:
//
//	jws.Sign(payload, jws.WithKey(algorithm, key))
//	jws.Verify(serialized, jws.WithKey(algorithm, key))
//
// To sign, simply use `jws.Sign`. `payload` is a []byte buffer that
// contains whatever data you want to sign. `alg` is one of the
// jwa.SignatureAlgorithm constants from package jwa. For RSA and
// ECDSA family of algorithms, you will need to prepare a private key.
// For HMAC family, you just need a []byte value. The `jws.Sign`
// function will return the encoded JWS message on success.
//
// To verify, use `jws.Verify`. It will parse the `encodedjws` buffer
// and verify the result using `algorithm` and `key`. Upon successful
// verification, the original payload is returned, so you can work on it.
//
// As a sidenote, consider using github.com/lestrrat-go/htmsig if you
// looking for HTTP Message Signatures (RFC9421) -- it uses the same
// underlying signing/verification mechanisms as this module.
package jws

import (
	"bufio"
	"crypto/ecdh"
	"crypto/ecdsa"
	"crypto/ed25519"
	"crypto/rsa"
	"errors"
	"fmt"
	"io"
	"reflect"
	"sync"
	"unicode"
	"unicode/utf8"

	"github.com/lestrrat-go/jwx/v3/internal/base64"
	"github.com/lestrrat-go/jwx/v3/internal/json"
	"github.com/lestrrat-go/jwx/v3/internal/jwxio"
	"github.com/lestrrat-go/jwx/v3/internal/pool"
	"github.com/lestrrat-go/jwx/v3/internal/tokens"
	"github.com/lestrrat-go/jwx/v3/jwa"
	"github.com/lestrrat-go/jwx/v3/jwk"
	"github.com/lestrrat-go/jwx/v3/jws/jwsbb"
)

var registry = json.NewRegistry()

var signers = make(map[jwa.SignatureAlgorithm]Signer)
var muSigner = &sync.Mutex{}

func removeSigner(alg jwa.SignatureAlgorithm) {
	muSigner.Lock()
	defer muSigner.Unlock()
	delete(signers, alg)
}

type defaultSigner struct {
	alg jwa.SignatureAlgorithm
}

func (s defaultSigner) Algorithm() jwa.SignatureAlgorithm {
	return s.alg
}

func (s defaultSigner) Sign(key any, payload []byte) ([]byte, error) {
	return jwsbb.Sign(key, s.alg.String(), payload, nil)
}

type signerAdapter struct {
	signer Signer
}

func (s signerAdapter) Algorithm() jwa.SignatureAlgorithm {
	return s.signer.Algorithm()
}

func (s signerAdapter) Sign(key any, payload []byte) ([]byte, error) {
	return s.signer.Sign(payload, key)
}

const (
	fmtInvalid = 1 << iota
	fmtCompact
	fmtJSON
	fmtJSONPretty
	fmtMax
)

// silence linters
var _ = fmtInvalid
var _ = fmtMax

func validateKeyBeforeUse(key any) error {
	jwkKey, ok := key.(jwk.Key)
	if !ok {
		converted, err := jwk.Import(key)
		if err != nil {
			return fmt.Errorf(`could not convert key of type %T to jwk.Key for validation: %w`, key, err)
		}
		jwkKey = converted
	}
	return jwkKey.Validate()
}

// Sign generates a JWS message for the given payload and returns
// it in serialized form, which can be in either compact or
// JSON format. Default is compact.
//
// You must pass at least one key to `jws.Sign()` by using `jws.WithKey()`
// option.
//
//	jws.Sign(payload, jws.WithKey(alg, key))
//	jws.Sign(payload, jws.WithJSON(), jws.WithKey(alg1, key1), jws.WithKey(alg2, key2))
//
// Note that in the second example the `jws.WithJSON()` option is
// specified as well. This is because the compact serialization
// format does not support multiple signatures, and users must
// specifically ask for the JSON serialization format.
//
// Read the documentation for `jws.WithKey()` to learn more about the
// possible values that can be used for `alg` and `key`.
//
// You may create JWS messages with the "none" (jwa.NoSignature) algorithm
// if you use the `jws.WithInsecureNoSignature()` option. This option
// can be combined with one or more signature keys, as well as the
// `jws.WithJSON()` option to generate multiple signatures (though
// the usefulness of such constructs is highly debatable)
//
// Note that this library does not allow you to successfully call `jws.Verify()` on
// signatures with the "none" algorithm. To parse these, use `jws.Parse()` instead.
//
// If you want to use a detached payload, use `jws.WithDetachedPayload()` as
// one of the options. When you use this option, you must always set the
// first parameter (`payload`) to `nil`, or the function will return an error
//
// You may also want to look at how to pass protected headers to the
// signing process, as you will likely be required to set the `b64` field
// when using detached payload.
//
// Look for options that return `jws.SignOption` or `jws.SignVerifyOption`
// for a complete list of options that can be passed to this function.
//
// You can use `errors.Is` with `jws.SignError()` to check if an error is from this function.
func Sign(payload []byte, options ...SignOption) ([]byte, error) {
	sc := signContextPool.Get()
	defer signContextPool.Put(sc)

	sc.payload = payload

	if err := sc.ProcessOptions(options); err != nil {
		return nil, signerr(`failed to process options: %w`, err)
	}

	lsigner := len(sc.sigbuilders)
	if lsigner == 0 {
		return nil, signerr(`no signers available. Specify an algorithm and a key using jws.WithKey()`)
	}

	// Design note: while we could have easily set format = fmtJSON when
	// lsigner > 1, I believe the decision to change serialization formats
	// must be explicitly stated by the caller. Otherwise, I'm pretty sure
	// there would be people filing issues saying "I get JSON when I expected
	// compact serialization".
	//
	// Therefore, instead of making implicit format conversions, we force the
	// user to spell it out as `jws.Sign(..., jws.WithJSON(), jws.WithKey(...), jws.WithKey(...))`
	if sc.format == fmtCompact && lsigner != 1 {
		return nil, signerr(`cannot have multiple signers (keys) specified for compact serialization. Use only one jws.WithKey()`)
	}

	// Create a Message object with all the bits and bobs, and we'll
	// serialize it in the end
	var result Message

	if err := sc.PopulateMessage(&result); err != nil {
		return nil, signerr(`failed to populate message: %w`, err)
	}
	switch sc.format {
	case fmtJSON:
		return json.Marshal(result)
	case fmtJSONPretty:
		return json.MarshalIndent(result, "", "  ")
	case fmtCompact:
		// Take the only signature object, and convert it into a Compact
		// serialization format
		var compactOpts []CompactOption
		if sc.detached {
			compactOpts = append(compactOpts, WithDetached(true))
		}
		for _, option := range options {
			if copt, ok := option.(CompactOption); ok {
				compactOpts = append(compactOpts, copt)
			}
		}
		return Compact(&result, compactOpts...)
	default:
		return nil, signerr(`invalid serialization format`)
	}
}

var allowNoneWhitelist = jwk.WhitelistFunc(func(string) bool {
	return false
})

// Verify checks if the given JWS message is verifiable using `alg` and `key`.
// `key` may be a "raw" key (e.g. rsa.PublicKey) or a jwk.Key
//
// If the verification is successful, `err` is nil, and the content of the
// payload that was signed is returned. If you need more fine-grained
// control of the verification process, manually generate a
// `Verifier` in `verify` subpackage, and call `Verify` method on it.
// If you need to access signatures and JOSE headers in a JWS message,
// use `Parse` function to get `Message` object.
//
// Because the use of "none" (jwa.NoSignature) algorithm is strongly discouraged,
// this function DOES NOT consider it a success when `{"alg":"none"}` is
// encountered in the message (it would also be counterintuitive when the code says
// it _verified_ something when in fact it did no such thing). If you want to
// accept messages with "none" signature algorithm, use `jws.Parse` to get the
// raw JWS message.
//
// The error returned by this function is of type can be checked against
// `jws.VerifyError()` and `jws.VerificationError()`. The latter is returned
// when the verification process itself fails (e.g. invalid signature, wrong key),
// while the former is returned when any other part of the `jws.Verify()`
// function fails.
func Verify(buf []byte, options ...VerifyOption) ([]byte, error) {
	vc := verifyContextPool.Get()
	defer verifyContextPool.Put(vc)

	if err := vc.ProcessOptions(options); err != nil {
		return nil, verifyerr(`failed to process options: %w`, err)
	}

	return vc.VerifyMessage(buf)
}

// get the value of b64 header field.
// If the field does not exist, returns true (default)
// Otherwise return the value specified by the header field.
func getB64Value(hdr Headers) bool {
	var b64 bool
	if err := hdr.Get("b64", &b64); err != nil {
		return true // default
	}

	return b64
}

// Parse parses contents from the given source and creates a jws.Message
// struct. By default the input can be in either compact or full JSON serialization.
//
// You may pass `jws.WithJSON()` and/or `jws.WithCompact()` to specify
// explicitly which format to use. If neither or both is specified, the function
// will attempt to autodetect the format. If one or the other is specified,
// only the specified format will be attempted.
//
// On error, returns a jws.ParseError.
func Parse(src []byte, options ...ParseOption) (*Message, error) {
	var formats int
	for _, option := range options {
		switch option.Ident() {
		case identSerialization{}:
			var v int
			if err := option.Value(&v); err != nil {
				return nil, parseerr(`failed to retrieve serialization option value: %w`, err)
			}
			switch v {
			case fmtJSON:
				formats |= fmtJSON
			case fmtCompact:
				formats |= fmtCompact
			}
		}
	}

	// if format is 0 or both JSON/Compact, auto detect
	if v := formats & (fmtJSON | fmtCompact); v == 0 || v == fmtJSON|fmtCompact {
	CHECKLOOP:
		for i := range src {
			r := rune(src[i])
			if r >= utf8.RuneSelf {
				r, _ = utf8.DecodeRune(src)
			}
			if !unicode.IsSpace(r) {
				if r == tokens.OpenCurlyBracket {
					formats = fmtJSON
				} else {
					formats = fmtCompact
				}
				break CHECKLOOP
			}
		}
	}

	if formats&fmtCompact == fmtCompact {
		msg, err := parseCompact(src)
		if err != nil {
			return nil, parseerr(`failed to parse compact format: %w`, err)
		}
		return msg, nil
	} else if formats&fmtJSON == fmtJSON {
		msg, err := parseJSON(src)
		if err != nil {
			return nil, parseerr(`failed to parse JSON format: %w`, err)
		}
		return msg, nil
	}

	return nil, parseerr(`invalid byte sequence`)
}

// ParseString parses contents from the given source and creates a jws.Message
// struct. The input can be in either compact or full JSON serialization.
//
// On error, returns a jws.ParseError.
func ParseString(src string) (*Message, error) {
	msg, err := Parse([]byte(src))
	if err != nil {
		return nil, sparseerr(`failed to parse string: %w`, err)
	}
	return msg, nil
}

// ParseReader parses contents from the given source and creates a jws.Message
// struct. The input can be in either compact or full JSON serialization.
//
// On error, returns a jws.ParseError.
func ParseReader(src io.Reader) (*Message, error) {
	data, err := jwxio.ReadAllFromFiniteSource(src)
	if err == nil {
		return Parse(data)
	}

	if !errors.Is(err, jwxio.NonFiniteSourceError()) {
		return nil, rparseerr(`failed to read from finite source: %w`, err)
	}

	rdr := bufio.NewReader(src)
	var first rune
	for {
		r, _, err := rdr.ReadRune()
		if err != nil {
			return nil, rparseerr(`failed to read rune: %w`, err)
		}
		if !unicode.IsSpace(r) {
			first = r
			if err := rdr.UnreadRune(); err != nil {
				return nil, rparseerr(`failed to unread rune: %w`, err)
			}

			break
		}
	}

	var parser func(io.Reader) (*Message, error)
	if first == tokens.OpenCurlyBracket {
		parser = parseJSONReader
	} else {
		parser = parseCompactReader
	}

	m, err := parser(rdr)
	if err != nil {
		return nil, rparseerr(`failed to parse reader: %w`, err)
	}

	return m, nil
}

func parseJSONReader(src io.Reader) (result *Message, err error) {
	var m Message
	if err := json.NewDecoder(src).Decode(&m); err != nil {
		return nil, fmt.Errorf(`failed to unmarshal jws message: %w`, err)
	}
	return &m, nil
}

func parseJSON(data []byte) (result *Message, err error) {
	var m Message
	if err := json.Unmarshal(data, &m); err != nil {
		return nil, fmt.Errorf(`failed to unmarshal jws message: %w`, err)
	}
	return &m, nil
}

// SplitCompact splits a JWS in compact format and returns its three parts
// separately: protected headers, payload and signature.
// On error, returns a jws.ParseError.
//
// This function will be deprecated in v4. It is a low-level API, and
// thus will be available in the `jwsbb` package.
func SplitCompact(src []byte) ([]byte, []byte, []byte, error) {
	hdr, payload, signature, err := jwsbb.SplitCompact(src)
	if err != nil {
		return nil, nil, nil, parseerr(`%w`, err)
	}
	return hdr, payload, signature, nil
}

// SplitCompactString splits a JWT and returns its three parts
// separately: protected headers, payload and signature.
// On error, returns a jws.ParseError.
//
// This function will be deprecated in v4. It is a low-level API, and
// thus will be available in the `jwsbb` package.
func SplitCompactString(src string) ([]byte, []byte, []byte, error) {
	hdr, payload, signature, err := jwsbb.SplitCompactString(src)
	if err != nil {
		return nil, nil, nil, parseerr(`%w`, err)
	}
	return hdr, payload, signature, nil
}

// SplitCompactReader splits a JWT and returns its three parts
// separately: protected headers, payload and signature.
// On error, returns a jws.ParseError.
//
// This function will be deprecated in v4. It is a low-level API, and
// thus will be available in the `jwsbb` package.
func SplitCompactReader(rdr io.Reader) ([]byte, []byte, []byte, error) {
	hdr, payload, signature, err := jwsbb.SplitCompactReader(rdr)
	if err != nil {
		return nil, nil, nil, parseerr(`%w`, err)
	}
	return hdr, payload, signature, nil
}

// parseCompactReader parses a JWS value serialized via compact serialization.
func parseCompactReader(rdr io.Reader) (m *Message, err error) {
	protected, payload, signature, err := SplitCompactReader(rdr)
	if err != nil {
		return nil, fmt.Errorf(`invalid compact serialization format: %w`, err)
	}
	return parse(protected, payload, signature)
}

func parseCompact(data []byte) (m *Message, err error) {
	protected, payload, signature, err := SplitCompact(data)
	if err != nil {
		return nil, fmt.Errorf(`invalid compact serialization format: %w`, err)
	}
	return parse(protected, payload, signature)
}

func parse(protected, payload, signature []byte) (*Message, error) {
	decodedHeader, err := base64.Decode(protected)
	if err != nil {
		return nil, fmt.Errorf(`failed to decode protected headers: %w`, err)
	}

	hdr := NewHeaders()
	if err := json.Unmarshal(decodedHeader, hdr); err != nil {
		return nil, fmt.Errorf(`failed to parse JOSE headers: %w`, err)
	}

	var decodedPayload []byte
	b64 := getB64Value(hdr)
	if !b64 {
		decodedPayload = payload
	} else {
		v, err := base64.Decode(payload)
		if err != nil {
			return nil, fmt.Errorf(`failed to decode payload: %w`, err)
		}
		decodedPayload = v
	}

	decodedSignature, err := base64.Decode(signature)
	if err != nil {
		return nil, fmt.Errorf(`failed to decode signature: %w`, err)
	}

	var msg Message
	msg.payload = decodedPayload
	msg.signatures = append(msg.signatures, &Signature{
		protected: hdr,
		signature: decodedSignature,
	})
	msg.b64 = b64
	return &msg, nil
}

type CustomDecoder = json.CustomDecoder
type CustomDecodeFunc = json.CustomDecodeFunc

// RegisterCustomField allows users to specify that a private field
// be decoded as an instance of the specified type. This option has
// a global effect.
//
// For example, suppose you have a custom field `x-birthday`, which
// you want to represent as a string formatted in RFC3339 in JSON,
// but want it back as `time.Time`.
//
// In such case you would register a custom field as follows
//
//	jws.RegisterCustomField(`x-birthday`, time.Time{})
//
// Then you can use a `time.Time` variable to extract the value
// of `x-birthday` field, instead of having to use `any`
// and later convert it to `time.Time`
//
//	var bday time.Time
//	_ = hdr.Get(`x-birthday`, &bday)
//
// If you need a more fine-tuned control over the decoding process,
// you can register a `CustomDecoder`. For example, below shows
// how to register a decoder that can parse RFC1123 format string:
//
//	jws.RegisterCustomField(`x-birthday`, jws.CustomDecodeFunc(func(data []byte) (any, error) {
//	  return time.Parse(time.RFC1123, string(data))
//	}))
//
// Please note that use of custom fields can be problematic if you
// are using a library that does not implement MarshalJSON/UnmarshalJSON
// and you try to roundtrip from an object to JSON, and then back to an object.
// For example, in the above example, you can _parse_ time values formatted
// in the format specified in RFC822, but when you convert an object into
// JSON, it will be formatted in RFC3339, because that's what `time.Time`
// likes to do. To avoid this, it's always better to use a custom type
// that wraps your desired type (in this case `time.Time`) and implement
// MarshalJSON and UnmashalJSON.
func RegisterCustomField(name string, object any) {
	registry.Register(name, object)
}

// Helpers for signature verification
var rawKeyToKeyType = make(map[reflect.Type]jwa.KeyType)
var keyTypeToAlgorithms = make(map[jwa.KeyType][]jwa.SignatureAlgorithm)

func init() {
	rawKeyToKeyType[reflect.TypeFor[[]byte]()] = jwa.OctetSeq()
	rawKeyToKeyType[reflect.TypeFor[ed25519.PublicKey]()] = jwa.OKP()
	rawKeyToKeyType[reflect.TypeFor[rsa.PublicKey]()] = jwa.RSA()
	rawKeyToKeyType[reflect.TypeFor[*rsa.PublicKey]()] = jwa.RSA()
	rawKeyToKeyType[reflect.TypeFor[ecdsa.PublicKey]()] = jwa.EC()
	rawKeyToKeyType[reflect.TypeFor[*ecdsa.PublicKey]()] = jwa.EC()

	addAlgorithmForKeyType(jwa.OKP(), jwa.EdDSA())
	for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256(), jwa.HS384(), jwa.HS512()} {
		addAlgorithmForKeyType(jwa.OctetSeq(), alg)
	}
	for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256(), jwa.RS384(), jwa.RS512(), jwa.PS256(), jwa.PS384(), jwa.PS512()} {
		addAlgorithmForKeyType(jwa.RSA(), alg)
	}
	for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256(), jwa.ES384(), jwa.ES512()} {
		addAlgorithmForKeyType(jwa.EC(), alg)
	}
}

func addAlgorithmForKeyType(kty jwa.KeyType, alg jwa.SignatureAlgorithm) {
	keyTypeToAlgorithms[kty] = append(keyTypeToAlgorithms[kty], alg)
}

// AlgorithmsForKey returns the possible signature algorithms that can
// be used for a given key. It only takes in consideration keys/algorithms
// for verification purposes, as this is the only usage where one may need
// dynamically figure out which method to use.
func AlgorithmsForKey(key any) ([]jwa.SignatureAlgorithm, error) {
	var kty jwa.KeyType
	switch key := key.(type) {
	case jwk.Key:
		kty = key.KeyType()
	case rsa.PublicKey, *rsa.PublicKey, rsa.PrivateKey, *rsa.PrivateKey:
		kty = jwa.RSA()
	case ecdsa.PublicKey, *ecdsa.PublicKey, ecdsa.PrivateKey, *ecdsa.PrivateKey:
		kty = jwa.EC()
	case ed25519.PublicKey, ed25519.PrivateKey, *ecdh.PublicKey, ecdh.PublicKey, *ecdh.PrivateKey, ecdh.PrivateKey:
		kty = jwa.OKP()
	case []byte:
		kty = jwa.OctetSeq()
	default:
		return nil, fmt.Errorf(`unknown key type %T`, key)
	}

	algs, ok := keyTypeToAlgorithms[kty]
	if !ok {
		return nil, fmt.Errorf(`unregistered key type %q`, kty)
	}
	return algs, nil
}

// Settings allows you to set global settings for this JWS operations.
//
// Currently, the only setting available is `jws.WithLegacySigners()`,
// which for various reason is now a no-op.
func Settings(options ...GlobalOption) {
	for _, option := range options {
		switch option.Ident() {
		case identLegacySigners{}:
		}
	}
}

// VerifyCompactFast is a fast path verification function for JWS messages
// in compact serialization format.
//
// This function is considered experimental, and may change or be removed
// in the future.
//
// VerifyCompactFast performs signature verification on a JWS compact
// serialization without fully parsing the message into a jws.Message object.
// This makes it more efficient for cases where you only need to verify
// the signature and extract the payload, without needing access to headers
// or other JWS metadata.
//
// Returns the original payload that was signed if verification succeeds.
//
// Unlike jws.Verify(), this function requires you to specify the
// algorithm explicitly rather than extracting it from the JWS headers.
// This can be useful for performance-critical applications where the
// algorithm is known in advance.
//
// Since this function avoids doing many checks that jws.Verify would perform,
// you must ensure to perform the necessary checks including ensuring that algorithm is safe to use for your payload yourself.
func VerifyCompactFast(key any, compact []byte, alg jwa.SignatureAlgorithm) ([]byte, error) {
	algstr := alg.String()

	// Split the serialized JWT into its components
	hdr, payload, encodedSig, err := jwsbb.SplitCompact(compact)
	if err != nil {
		return nil, fmt.Errorf("jwt.verifyFast: failed to split compact: %w", err)
	}

	signature, err := base64.Decode(encodedSig)
	if err != nil {
		return nil, fmt.Errorf("jwt.verifyFast: failed to decode signature: %w", err)
	}

	// Instead of appending, copy the data from hdr/payload
	lvb := len(hdr) + 1 + len(payload)
	verifyBuf := pool.ByteSlice().GetCapacity(lvb)
	verifyBuf = verifyBuf[:lvb]
	copy(verifyBuf, hdr)
	verifyBuf[len(hdr)] = tokens.Period
	copy(verifyBuf[len(hdr)+1:], payload)
	defer pool.ByteSlice().Put(verifyBuf)

	// Verify the signature
	if verifier2, err := VerifierFor(alg); err == nil {
		if err := verifier2.Verify(key, verifyBuf, signature); err != nil {
			return nil, verifyError{verificationError{fmt.Errorf("jwt.VerifyCompact: signature verification failed for %s: %w", algstr, err)}}
		}
	} else {
		legacyVerifier, err := NewVerifier(alg)
		if err != nil {
			return nil, verifyerr("jwt.VerifyCompact: failed to create verifier for %s: %w", algstr, err)
		}
		if err := legacyVerifier.Verify(verifyBuf, signature, key); err != nil {
			return nil, verifyError{verificationError{fmt.Errorf("jwt.VerifyCompact: signature verification failed for %s: %w", algstr, err)}}
		}
	}

	decoded, err := base64.Decode(payload)
	if err != nil {
		return nil, verifyerr("jwt.VerifyCompact: failed to decode payload: %w", err)
	}
	return decoded, nil
}
