<|start_filename|>go/pkg/oberon/public_key.go<|end_filename|>
package oberon

import (
	"encoding/json"
	"fmt"
	bls12381 "github.com/mikelodder7/bls12-381"
)

var (
	g1    = bls12381.NewG1()
	g2    = bls12381.NewG2()
	genG2 = g2.One()
)

type PublicKey struct {
	W *bls12381.PointG2
	X *bls12381.PointG2
	Y *bls12381.PointG2
}

func (p *PublicKey) Verify(id []byte, t *Token) error {
	return t.Verify(p, id)
}

func (p *PublicKey) FromSecretKey(sk *SecretKey) {
	p.W = g2.MulScalar(g2.New(), g2.One(), sk.W)
	p.X = g2.MulScalar(g2.New(), g2.One(), sk.X)
	p.Y = g2.MulScalar(g2.New(), g2.One(), sk.Y)
}

func (p PublicKey) MarshalBinary() ([]byte, error) {
	return append(append(g2.ToCompressed(p.W), g2.ToCompressed(p.X)...), g2.ToCompressed(p.Y)...), nil
}

func (p *PublicKey) UnmarshalBinary(in []byte) error {
	if len(in) != 288 {
		return fmt.Errorf("invalid length")
	}
	w, err := g2.FromCompressed(in[:96])
	if err != nil {
		return err
	}
	x, err := g2.FromCompressed(in[96:192])
	if err != nil {
		return err
	}
	y, err := g2.FromCompressed(in[192:])
	if err != nil {
		return err
	}

	goodW := isValidPointG2(w)
	goodX := isValidPointG2(x)
	goodY := isValidPointG2(y)
	if goodW && goodX && goodY {
		p.W = w
		p.X = x
		p.Y = y
		return nil
	}
	return fmt.Errorf("invalid public key")
}

func (p PublicKey) MarshalText() ([]byte, error) {
	tmp := map[string][]byte{
		"w": g2.ToCompressed(p.W),
		"x": g2.ToCompressed(p.X),
		"y": g2.ToCompressed(p.Y),
	}
	return json.Marshal(&tmp)
}

func (p *PublicKey) UnmarshalText(in []byte) error {
	var tmp map[string][]byte
	var w, x, y *bls12381.PointG2

	err := json.Unmarshal(in, &tmp)
	if err != nil {
		return err
	}
	if wBytes, ok := tmp["w"]; ok {
		w, err = g2.FromCompressed(wBytes)
		if err != nil {
			return nil
		}
	} else {
		return fmt.Errorf("missing expected map key 'w'")
	}

	if xBytes, ok := tmp["x"]; ok {
		x, err = g2.FromCompressed(xBytes)
		if err != nil {
			return nil
		}
	} else {
		return fmt.Errorf("missing expected map key 'x'")
	}

	if yBytes, ok := tmp["y"]; ok {
		y, err = g2.FromCompressed(yBytes)
		if err != nil {
			return nil
		}
	} else {
		return fmt.Errorf("missing expected map key 'y'")
	}

	goodW := isValidPointG2(w)
	goodX := isValidPointG2(x)
	goodY := isValidPointG2(y)

	if goodW && goodX && goodY {
		p.W = w
		p.X = x
		p.Y = y
		return nil
	}
	return fmt.Errorf("invalid secret key")
}

func isValidPointG2(p *bls12381.PointG2) bool {
	return g2.InCorrectSubgroup(p) && g2.IsOnCurve(p) && !g2.IsZero(p)
}


<|start_filename|>go/pkg/oberon/proof_test.go<|end_filename|>
package oberon

import (
	crand "crypto/rand"
	"encoding/hex"
	"github.com/stretchr/testify/require"
	"testing"
)

func TestProof(t *testing.T) {
	// Test values don't use as real world values
	nonce := []byte{138, 162, 3, 91, 76, 34, 240, 157, 149, 94, 93, 228, 214, 51, 50, 136}
	skBytes := []byte{180, 92, 239, 44, 240, 143, 149, 163, 45, 177, 22, 179, 146, 120, 129, 229, 78, 56, 70, 205, 251, 160, 140, 79, 159, 138, 6, 56, 250, 236, 176, 11, 70, 53, 138, 199, 245, 180, 223, 213, 128, 166, 122, 225, 67, 58, 138, 201, 19, 114, 57, 149, 70, 141, 31, 45, 180, 30, 208, 222, 234, 112, 21, 34, 37, 5, 163, 172, 96, 40, 81, 27, 89, 86, 163, 93, 15, 201, 200, 183, 157, 18, 134, 140, 156, 43, 79, 231, 42, 234, 198, 139, 130, 52, 176, 106}
	sk := new(SecretKey)
	err := sk.UnmarshalBinary(skBytes)
	require.NoError(t, err)
	pk := sk.PublicKey()
	require.NotNil(t, pk)

	token, err := NewToken(sk, testId)
	require.NoError(t, err)
	require.NotNil(t, token)
	require.NoError(t, pk.Verify(testId, token))
	blinding, err := NewBlinding([]byte("1234"))
	require.NoError(t, err)
	require.NotNil(t, blinding)

	blindedToken := new(Token).ApplyBlinding(token, blinding)
	proof, err := NewProof(blindedToken, []*Blinding{blinding}, testId, nonce, crand.Reader)
	require.NoError(t, err)
	require.NotNil(t, proof)
	require.NoError(t, proof.Open(pk, testId, nonce))
	require.Error(t, proof.Open(pk, []byte("wrong id"), nonce))
	require.Error(t, proof.Open(pk, testId, []byte("wrong nonce")))
	// No blindings
	proof, err = NewProof(blindedToken, []*Blinding{}, testId, nonce, crand.Reader)
	require.NoError(t, err)
	require.Error(t, proof.Open(pk, testId, nonce))

	blindedToken = token.ApplyBlinding(token, blinding)
	proof, err = NewProof(blindedToken, []*Blinding{blinding}, testId, nonce, crand.Reader)
	require.NoError(t, err)
	require.NotNil(t, proof)
	require.NoError(t, proof.Open(pk, testId, nonce))
	require.Error(t, proof.Open(pk, []byte("wrong id"), nonce))
	require.Error(t, proof.Open(pk, testId, []byte("wrong nonce")))
	// No blindings
	proof, err = NewProof(blindedToken, []*Blinding{}, testId, nonce, crand.Reader)
	require.NoError(t, err)
	require.Error(t, proof.Open(pk, testId, nonce))
}

func TestProof2(t *testing.T) {
	id, _ := hex.DecodeString("aa")
	skBytes, _ := hex.DecodeString("b45cef2cf08f95a32db116b3927881e54e3846cdfba08c4f9f8a0638faecb00b46358ac7f5b4dfd580a67ae1433a8ac913723995468d1f2db41ed0deea7015222505a3ac6028511b5956a35d0fc9c8b79d12868c9c2b4fe72aeac68b8234b06a")
	sk := new(SecretKey)
	_ = sk.UnmarshalBinary(skBytes)
	nonce, _ := hex.DecodeString("8aa2035b4c22f09d955e5de4d6333288")
	tokenBytes, _ := hex.DecodeString("<KEY>")
	proofBytes, _ := hex.DecodeString("a99748bd7e02802827aea3a4298b721f2d2e8a5d359655deadcb416d4e515ca9cd9291b83f8e4a159aaa132ae57f251a80925021351b7fe3a64b6744324eb1ad1940f9ab6d76ca2055c55fa8d4a9c7448be5049a3c856b930b3a1c5633d7c1ec")
	token := new(Token)
	proof := new(Proof)

	_ = token.UnmarshalBinary(tokenBytes)
	_ = proof.UnmarshalBinary(proofBytes)
	pk := sk.PublicKey()

	require.NoError(t, proof.Open(pk, id, nonce))
}


<|start_filename|>go/pkg/oberon/proof.go<|end_filename|>
package oberon

import (
	"encoding/json"
	"fmt"
	bls12381 "github.com/mikelodder7/bls12-381"
	"io"
)

type Proof struct {
	Proof, UTick *bls12381.PointG1
}

func NewProof(token *Token, blindings []*Blinding, id, nonce []byte, rng io.Reader) (*Proof, error) {
	p := new(Proof)
	err := p.Create(token, blindings, id, nonce, rng)
	return p, err
}

func (p *Proof) Create(
	token *Token,
	blindings []*Blinding,
	id, nonce []byte,
	rng io.Reader,
) error {
	m, err := computeM(id)
	if err != nil {
		return err
	}
	mTick, err := computeMTick(m)
	if err != nil {
		return err
	}
	u, err := computeU(mTick)
	if err != nil {
		return err
	}
	t, err := hashToScalar([][]byte{id, nonce})
	if err != nil {
		return err
	}
	if t.IsZero() || t.IsOne() {
		return fmt.Errorf("invalid t value")
	}
	r, err := bls12381.NewFr().Rand(rng)
	if err != nil {
		return err
	}
	attempts := 0
	for ; (r.IsZero() || r.IsOne()) && attempts < 100; attempts++ {
		r, err = bls12381.NewFr().Rand(rng)
		if err != nil {
			return err
		}
	}
	if attempts == 100 {
		return fmt.Errorf("invalid r value")
	}

	uTick := g1.MulScalar(g1.New(), u, r)
	points := make([]*bls12381.PointG1, 0, 2+len(blindings))
	scalars := make([]*bls12381.Fr, 0, 2+len(blindings))

	points = append(points, uTick)
	scalars = append(scalars, t)

	points = append(points, token.Value)
	scalars = append(scalars, r)

	for _, b := range blindings {
		points = append(points, b.Value)
		scalars = append(scalars, r)
	}

	proof, err := g1.MultiExp(g1.New(), points, scalars)
	if err != nil {
		return nil
	}

	p.Proof = proof
	p.UTick = uTick
	return nil
}

func (p Proof) Open(
	pk *PublicKey,
	id, nonce []byte,
) error {
	goodProof := isValidPointG1(p.Proof)
	goodUTick := isValidPointG1(p.UTick)

	if !goodProof || !goodUTick {
		return fmt.Errorf("invalid proof")
	}

	m, err := computeM(id)
	if err != nil {
		return err
	}
	mTick, err := computeMTick(m)
	if err != nil {
		return err
	}
	t, err := hashToScalar([][]byte{id, nonce})
	if err != nil {
		return err
	}
	if t.IsZero() || t.IsOne() {
		return fmt.Errorf("invalid t value")
	}

	rhs, err := g2.MultiExp(g2.New(),
		[]*bls12381.PointG2{pk.W, pk.X, pk.Y, genG2},
		[]*bls12381.Fr{mTick, bls12381.NewFr().One(), m, t})
	if err != nil {
		return err
	}
	engine := bls12381.NewEngine()
	engine.AddPairInv(p.UTick, rhs)
	engine.AddPair(p.Proof, genG2)
	if engine.Check() {
		return nil
	} else {
		return fmt.Errorf("check failed")
	}
}

func (p Proof) MarshalBinary() ([]byte, error) {
	var tmp [96]byte
	copy(tmp[:48], g1.ToCompressed(p.Proof))
	copy(tmp[48:], g1.ToCompressed(p.UTick))
	return tmp[:], nil
}

func (p *Proof) UnmarshalBinary(in []byte) error {
	if len(in) != 96 {
		return fmt.Errorf("invalid length")
	}
	proof, err := g1.FromCompressed(in[:48])
	if err != nil {
		return nil
	}
	uTick, err := g1.FromCompressed(in[48:])
	if err != nil {
		return nil
	}
	goodProof := isValidPointG1(proof)
	goodUTick := isValidPointG1(uTick)
	if goodProof && goodUTick {
		p.Proof = proof
		p.UTick = uTick
		return nil
	}
	return fmt.Errorf("invalid proof")
}

func (p Proof) MarshalText() ([]byte, error) {
	tmp := map[string][]byte{
		"proof":  g1.ToCompressed(p.Proof),
		"u_tick": g1.ToCompressed(p.UTick),
	}
	return json.Marshal(&tmp)
}

func (p *Proof) UnmarshalText(in []byte) error {
	var tmp map[string][]byte
	var proof, uTick *bls12381.PointG1

	err := json.Unmarshal(in, &tmp)
	if err != nil {
		return err
	}
	if proofBytes, ok := tmp["proof"]; ok {
		proof, err = g1.FromCompressed(proofBytes)
		if err != nil {
			return err
		}
	} else {
		return fmt.Errorf("missing expected map key 'proof'")
	}

	if uTickBytes, ok := tmp["u_tick"]; ok {
		uTick, err = g1.FromCompressed(uTickBytes)
		if err != nil {
			return err
		}
	} else {
		return fmt.Errorf("missing expected map key 'u_tick'")
	}

	goodProof := isValidPointG1(proof)
	goodUTick := isValidPointG1(uTick)
	if goodProof && goodUTick {
		p.Proof = proof
		p.UTick = uTick
		return nil
	}
	return fmt.Errorf("invalid proof")
}
