// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Copyright 2016 Google Inc. All Rights Reserved.
//
// 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 secp256k1 implements a verifiable random function using curve secp256k1.
package secp256k1

import (
	"bytes"
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/binary"
	"errors"
	"math/big"

	vrfp "github.com/33cn/chain33/common/vrf"
	"github.com/btcsuite/btcd/btcec"
)

var (
	curve = btcec.S256()
	// ErrInvalidVRF err
	ErrInvalidVRF = errors.New("invalid VRF proof")
)

// PublicKey holds a public VRF key.
type PublicKey struct {
	*ecdsa.PublicKey
}

// PrivateKey holds a private VRF key.
type PrivateKey struct {
	*ecdsa.PrivateKey
}

// GenerateKey generates a fresh keypair for this VRF
func GenerateKey() (vrfp.PrivateKey, vrfp.PublicKey) {
	key, err := ecdsa.GenerateKey(curve, rand.Reader)
	if err != nil {
		return nil, nil
	}

	return &PrivateKey{PrivateKey: key}, &PublicKey{PublicKey: &key.PublicKey}
}

// H1 hashes m to a curve point
func H1(m []byte) (x, y *big.Int) {
	h := sha512.New()
	var i uint32
	byteLen := (curve.BitSize + 7) >> 3
	for x == nil && i < 100 {
		// TODO: Use a NIST specified DRBG.
		h.Reset()
		if err := binary.Write(h, binary.BigEndian, i); err != nil {
			panic(err)
		}
		if _, err := h.Write(m); err != nil {
			panic(err)
		}
		r := []byte{2} // Set point encoding to "compressed", y=0.
		r = h.Sum(r)
		x, y = Unmarshal(curve, r[:byteLen+1])
		i++
	}
	return
}

var one = big.NewInt(1)

// H2 hashes to an integer [1,N-1]
func H2(m []byte) *big.Int {
	// NIST SP 800-90A § A.5.1: Simple discard method.
	byteLen := (curve.BitSize + 7) >> 3
	h := sha512.New()
	for i := uint32(0); ; i++ {
		// TODO: Use a NIST specified DRBG.
		h.Reset()
		if err := binary.Write(h, binary.BigEndian, i); err != nil {
			panic(err)
		}
		if _, err := h.Write(m); err != nil {
			panic(err)
		}
		b := h.Sum(nil)
		k := new(big.Int).SetBytes(b[:byteLen])
		if k.Cmp(new(big.Int).Sub(curve.N, one)) == -1 {
			return k.Add(k, one)
		}
	}
}

// Evaluate returns the verifiable unpredictable function evaluated at m
func (k PrivateKey) Evaluate(m []byte) (index [32]byte, proof []byte) {
	nilIndex := [32]byte{}
	// Prover chooses r <-- [1,N-1]
	r, _, _, err := elliptic.GenerateKey(curve, rand.Reader)
	if err != nil {
		return nilIndex, nil
	}
	ri := new(big.Int).SetBytes(r)

	// H = H1(m)
	Hx, Hy := H1(m)

	// VRF_k(m) = [k]H
	sHx, sHy := curve.ScalarMult(Hx, Hy, k.D.Bytes())
	vrf := elliptic.Marshal(curve, sHx, sHy) // 65 bytes.

	// G is the base point
	// s = H2(G, H, [k]G, VRF, [r]G, [r]H)
	rGx, rGy := curve.ScalarBaseMult(r)
	rHx, rHy := curve.ScalarMult(Hx, Hy, r)
	var b bytes.Buffer
	if _, err := b.Write(elliptic.Marshal(curve, curve.Gx, curve.Gy)); err != nil {
		panic(err)
	}
	if _, err := b.Write(elliptic.Marshal(curve, Hx, Hy)); err != nil {
		panic(err)
	}
	if _, err := b.Write(elliptic.Marshal(curve, k.PublicKey.X, k.PublicKey.Y)); err != nil {
		panic(err)
	}
	if _, err := b.Write(vrf); err != nil {
		panic(err)
	}
	if _, err := b.Write(elliptic.Marshal(curve, rGx, rGy)); err != nil {
		panic(err)
	}
	if _, err := b.Write(elliptic.Marshal(curve, rHx, rHy)); err != nil {
		panic(err)
	}
	s := H2(b.Bytes())

	// t = r−s*k mod N
	t := new(big.Int).Sub(ri, new(big.Int).Mul(s, k.D))
	t.Mod(t, curve.N)

	// Index = H(vrf)
	index = sha256.Sum256(vrf)

	// Write s, t, and vrf to a proof blob. Also write leading zeros before s and t
	// if needed.
	var buf bytes.Buffer
	if _, err := buf.Write(make([]byte, 32-len(s.Bytes()))); err != nil {
		panic(err)
	}
	if _, err := buf.Write(s.Bytes()); err != nil {
		panic(err)
	}
	if _, err := buf.Write(make([]byte, 32-len(t.Bytes()))); err != nil {
		panic(err)
	}
	if _, err := buf.Write(t.Bytes()); err != nil {
		panic(err)
	}
	if _, err := buf.Write(vrf); err != nil {
		panic(err)
	}

	return index, buf.Bytes()
}

// ProofToHash asserts that proof is correct for m and outputs index.
func (pk *PublicKey) ProofToHash(m, proof []byte) (index [32]byte, err error) {
	nilIndex := [32]byte{}
	// verifier checks that s == H2(m, [t]G + [s]([k]G), [t]H1(m) + [s]VRF_k(m))
	if got, want := len(proof), 64+65; got != want {
		return nilIndex, ErrInvalidVRF
	}

	// Parse proof into s, t, and vrf.
	s := proof[0:32]
	t := proof[32:64]
	vrf := proof[64 : 64+65]

	uHx, uHy := elliptic.Unmarshal(curve, vrf)
	if uHx == nil {
		return nilIndex, ErrInvalidVRF
	}

	// [t]G + [s]([k]G) = [t+ks]G
	tGx, tGy := curve.ScalarBaseMult(t)
	ksGx, ksGy := curve.ScalarMult(pk.X, pk.Y, s)
	tksGx, tksGy := curve.Add(tGx, tGy, ksGx, ksGy)

	// H = H1(m)
	// [t]H + [s]VRF = [t+ks]H
	Hx, Hy := H1(m)
	tHx, tHy := curve.ScalarMult(Hx, Hy, t)
	sHx, sHy := curve.ScalarMult(uHx, uHy, s)
	tksHx, tksHy := curve.Add(tHx, tHy, sHx, sHy)

	//   H2(G, H, [k]G, VRF, [t]G + [s]([k]G), [t]H + [s]VRF)
	// = H2(G, H, [k]G, VRF, [t+ks]G, [t+ks]H)
	// = H2(G, H, [k]G, VRF, [r]G, [r]H)
	var b bytes.Buffer
	if _, err := b.Write(elliptic.Marshal(curve, curve.Gx, curve.Gy)); err != nil {
		panic(err)
	}
	if _, err := b.Write(elliptic.Marshal(curve, Hx, Hy)); err != nil {
		panic(err)
	}
	if _, err := b.Write(elliptic.Marshal(curve, pk.X, pk.Y)); err != nil {
		panic(err)
	}
	if _, err := b.Write(vrf); err != nil {
		panic(err)
	}
	if _, err := b.Write(elliptic.Marshal(curve, tksGx, tksGy)); err != nil {
		panic(err)
	}
	if _, err := b.Write(elliptic.Marshal(curve, tksHx, tksHy)); err != nil {
		panic(err)
	}
	h2 := H2(b.Bytes())

	// Left pad h2 with zeros if needed. This will ensure that h2 is padded
	// the same way s is.
	var buf bytes.Buffer
	if _, err := buf.Write(make([]byte, 32-len(h2.Bytes()))); err != nil {
		panic(err)
	}
	if _, err := buf.Write(h2.Bytes()); err != nil {
		panic(err)
	}

	if !hmac.Equal(s, buf.Bytes()) {
		return nilIndex, ErrInvalidVRF
	}
	return sha256.Sum256(vrf), nil
}

// Public returns the corresponding public key as bytes.
func (k PrivateKey) Public() crypto.PublicKey {
	return &k.PublicKey
}
