// Copyright 2020 Google LLC
//
// 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.

package subtle

import (
	"errors"
	"fmt"
	"math/big"

	"github.com/tink-crypto/tink-go/v2/internal/signature/ecdsa"
)

var errUnsupportedEncoding = errors.New("ecdsa: unsupported encoding")

// ECDSASignature is a struct holding the r and s values of an ECDSA signature.
type ECDSASignature struct {
	R, S *big.Int
}

// NewECDSASignature creates a new ECDSASignature instance.
func NewECDSASignature(r, s *big.Int) *ECDSASignature {
	return &ECDSASignature{R: r, S: s}
}

// EncodeECDSASignature converts the signature to the given encoding format.
func (sig *ECDSASignature) EncodeECDSASignature(encoding, curveName string) ([]byte, error) {
	s := &ecdsa.Signature{R: sig.R, S: sig.S}
	var enc []byte
	var err error
	switch encoding {
	case "IEEE_P1363":
		enc, err = ecdsa.IEEEP1363Encode(s, curveName)
	case "DER":
		enc, err = ecdsa.ASN1Encode(s)
	default:
		err = errUnsupportedEncoding
	}
	if err != nil {
		return nil, fmt.Errorf("ecdsa: can't convert ECDSA signature to %s encoding: %v", encoding, err)
	}
	return enc, nil
}

// DecodeECDSASignature creates a new ECDSA signature using the given byte slice.
// The function assumes that the byte slice is the concatenation of the BigEndian
// representation of two big integer r and s.
func DecodeECDSASignature(encodedBytes []byte, encoding string) (*ECDSASignature, error) {
	var sig *ecdsa.Signature
	var err error
	switch encoding {
	case "IEEE_P1363":
		sig, err = ecdsa.IEEEP1363Decode(encodedBytes)
	case "DER":
		sig, err = ecdsa.ASN1Decode(encodedBytes)
	default:
		err = errUnsupportedEncoding
	}
	if err != nil {
		return nil, fmt.Errorf("ecdsa: %s", err)
	}
	return &ECDSASignature{R: sig.R, S: sig.S}, nil
}

// ValidateECDSAParams validates ECDSA parameters.
// The hash's strength must not be weaker than the curve's strength.
// DER and IEEE_P1363 encodings are supported.
func ValidateECDSAParams(hashAlg string, curve string, encoding string) error {
	switch encoding {
	case "DER":
	case "IEEE_P1363":
	default:
		return errUnsupportedEncoding
	}
	switch curve {
	case "NIST_P256":
		if hashAlg != "SHA256" {
			return errors.New("invalid hash type, expect SHA-256")
		}
	case "NIST_P384":
		if hashAlg != "SHA384" && hashAlg != "SHA512" {
			return errors.New("invalid hash type, expect SHA-384 or SHA-512")
		}
	case "NIST_P521":
		if hashAlg != "SHA512" {
			return errors.New("invalid hash type, expect SHA-512")
		}
	default:
		return fmt.Errorf("unsupported curve: %s", curve)
	}
	return nil
}
