// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.

package peertls

// Many cryptography standards use ASN.1 to define their data structures,
// and Distinguished Encoding Rules (DER) to serialize those structures.
// Because DER produces binary output, it can be challenging to transmit
// the resulting files through systems, like electronic mail, that only
// support ASCII. The PEM format solves this problem by encoding the
// binary data using base64.
// (see https://en.wikipedia.org/wiki/Privacy-enhanced_Electronic_Mail)

import (
	"crypto"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"math/big"

	"github.com/zeebo/errs"

	"storj.io/common/pkcrypto"
)

// NonTemporaryError is an error with a `Temporary` method which always returns false.
// It is intended for use with grpc.
//
// (see https://godoc.org/google.golang.org/grpc#WithDialer
// and https://godoc.org/google.golang.org/grpc#FailOnNonTempDialError).
type NonTemporaryError struct{ error }

// NewNonTemporaryError returns a new temporary error for use with grpc.
func NewNonTemporaryError(err error) NonTemporaryError {
	return NonTemporaryError{
		error: errs.Wrap(err),
	}
}

// DoubleSHA256PublicKey returns the hash of the hash of (double-hash, SHA226)
// the binary format of the given public key.
func DoubleSHA256PublicKey(k crypto.PublicKey) ([sha256.Size]byte, error) {
	kb, err := x509.MarshalPKIXPublicKey(k)
	if err != nil {
		return [sha256.Size]byte{}, err
	}
	mid := sha256.Sum256(kb)
	end := sha256.Sum256(mid[:])
	return end, nil
}

// Temporary returns false to indicate that this is a non-temporary error.
func (nte NonTemporaryError) Temporary() bool {
	return false
}

// Err returns the underlying error.
func (nte NonTemporaryError) Err() error {
	return nte.error
}

func verifyChainSignatures(certs []*x509.Certificate) error {
	for i, cert := range certs {
		j := len(certs)
		if i+1 < j {
			err := verifyCertSignature(certs[i+1], cert)
			if err != nil {
				return ErrVerifyCertificateChain.Wrap(err)
			}

			continue
		}

		err := verifyCertSignature(cert, cert)
		if err != nil {
			return ErrVerifyCertificateChain.Wrap(err)
		}

	}

	return nil
}

func verifyCertSignature(parentCert, childCert *x509.Certificate) error {
	return pkcrypto.HashAndVerifySignature(parentCert.PublicKey, childCert.RawTBSCertificate, childCert.Signature)
}

func newSerialNumber() (*big.Int, error) {
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, errs.New("failed to generateServerTls serial number: %s", err.Error())
	}

	return serialNumber, nil
}
