package privacypass

import (
	"crypto/rand"
	"git.openprivacy.ca/cwtch.im/tapir/primitives/core"
	ristretto "github.com/gtank/ristretto255"
)

// DLEQProof encapsulates a Chaum-Pedersen DLEQ Proof
// gut In Ernest F. Brickell, editor,CRYPTO’92,volume 740 ofLNCS, pages 89–105. Springer, Heidelberg,August 1993
type DLEQProof struct {
	C *ristretto.Scalar
	S *ristretto.Scalar
}

// DiscreteLogEquivalenceProof constructs a valid DLEQProof for the given parameters and transcript
// Given  Y = kX & Q = kP
// Peggy: t := choose randomly from Zq
//
//	A := tX
//	B := tP
//	c := H(transcript(X,Y,P,Q,A,B))
//	s := (t + ck) mod q
//
// Sends c,s to Vicky
func DiscreteLogEquivalenceProof(k *ristretto.Scalar, X *ristretto.Element, Y *ristretto.Element, P *ristretto.Element, Q *ristretto.Element, transcript *core.Transcript) DLEQProof {
	private := make([]byte, 64)
	rand.Read(private)
	t, err := new(ristretto.Scalar).SetUniformBytes(private)
	if err != nil {
		return DLEQProof{ristretto.NewScalar(), ristretto.NewScalar()}
	}
	A := new(ristretto.Element).ScalarMult(t, X)
	B := new(ristretto.Element).ScalarMult(t, P)

	transcript.AddToTranscript(DLEQX, X.Bytes())
	transcript.AddToTranscript(DLEQY, Y.Bytes())
	transcript.AddToTranscript(DLEQP, P.Bytes())
	transcript.AddToTranscript(DLEQQ, Q.Bytes())
	transcript.AddToTranscript(DLEQA, A.Bytes())
	transcript.AddToTranscript(DLEQB, B.Bytes())

	c := transcript.CommitToTranscriptScalar("c")
	s := new(ristretto.Scalar).Subtract(t, new(ristretto.Scalar).Multiply(c, k))
	return DLEQProof{c, s}
}

// VerifyDiscreteLogEquivalenceProof verifies the DLEQ for the given parameters and transcript
// Given  Y = kX & Q = kP  and Proof = (c,s)
// Vicky: X' := sX
//
//	Y' := cY
//	P' := sP
//	Q' := cQ
//	A' = X'+Y' == sX + cY ?= sG + ckG == (s+ck)X == tX == A
//	B' = P'+Q' == sP + cQ ?= sP + ckP == (s+ck)P == tP == B
//	c' := H(transcript(X,Y,P,Q,A',B'))
//
// Tests c ?= c
func VerifyDiscreteLogEquivalenceProof(dleq DLEQProof, X *ristretto.Element, Y *ristretto.Element, P *ristretto.Element, Q *ristretto.Element, transcript *core.Transcript) bool {

	Xs := new(ristretto.Element).ScalarMult(dleq.S, X)
	Yc := new(ristretto.Element).ScalarMult(dleq.C, Y)
	Ps := new(ristretto.Element).ScalarMult(dleq.S, P)
	Qc := new(ristretto.Element).ScalarMult(dleq.C, Q)

	A := new(ristretto.Element).Add(Xs, Yc)
	B := new(ristretto.Element).Add(Ps, Qc)

	transcript.AddToTranscript(DLEQX, X.Bytes())
	transcript.AddToTranscript(DLEQY, Y.Bytes())
	transcript.AddToTranscript(DLEQP, P.Bytes())
	transcript.AddToTranscript(DLEQQ, Q.Bytes())
	transcript.AddToTranscript(DLEQA, A.Bytes())
	transcript.AddToTranscript(DLEQB, B.Bytes())

	return transcript.CommitToTranscriptScalar("c").Equal(dleq.C) == 1
}
