Skip to content

Commit

Permalink
Merge pull request #65 from oasisprotocol/feature/expose-merlin
Browse files Browse the repository at this point in the history
primitives/merlin: Move from an internal package
  • Loading branch information
Yawning authored Jun 9, 2021
2 parents b872043 + 8698258 commit 9075215
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 79 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ does not escape this author.
* primitives/x25519: A X25519 implementation like `x/crypto/curve25519`.
* primitives/ed25519: A Ed25519 implementation like `crypto/ed25519`.
* primitives/sr25519: A sr25519 implementation like `https://github.com/w3f/schnorrkel`.
* primitives/merlin: A Merlin transcript implementation.

#### Ed25519 verification semantics

Expand Down
57 changes: 45 additions & 12 deletions internal/merlin/merlin.go → primitives/merlin/merlin.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Package merlin implements Merlin proof transcripts.
package merlin

import (
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"math"

"github.com/oasisprotocol/curve25519-voi/internal/strobe"
)
Expand All @@ -45,16 +47,18 @@ const (
domainSeparatorLabel = "dom-sep"
)

// Transcript is a Merlin proof transcript.
type Transcript struct {
s strobe.Strobe
}

// NewTranscript initializes a new transcript with the specified protocol label.
func NewTranscript(appLabel string) *Transcript {
t := Transcript{
s: strobe.New(merlinProtocolLabel),
}

t.AppendMessage([]byte(domainSeparatorLabel), []byte(appLabel))
t.AppendMessage(domainSeparatorLabel, []byte(appLabel))
return &t
}

Expand All @@ -66,27 +70,44 @@ func (t *Transcript) Clone() *Transcript {
}

// Append adds the message to the transcript with the supplied label.
func (t *Transcript) AppendMessage(label, message []byte) {
// If the length of label or message will overflow a 32-bit unsigned
// integer this method will panic.
func (t *Transcript) AppendMessage(label string, message []byte) {
if lLen := uint64(len(label)); lLen > math.MaxUint32 {
panic("merlin: label length exceeds limits")
}
if mLen := uint64(len(message)); mLen > math.MaxUint32 {
panic("merlin: message length exceeds limits")
}

// AD[label || le32(len(message))](message)

var sizeBuffer [4]byte
binary.LittleEndian.PutUint32(sizeBuffer[0:], uint32(len(message)))

t.s.MetaAD(label, false)
t.s.MetaAD([]byte(label), false)
t.s.MetaAD(sizeBuffer[:], true)

t.s.AD(message, false)
}

// ExtractBytes returns a buffer filled with the verifier's challenge bytes.
// The label parameter is metadata about the challenge, and is also appended to
// ExtractBytes fills dest with the verifier's challenge bytes. The label
// parameter is metadata about the challenge, and is also appended to
// the transcript. See the Transcript Protocols section of the Merlin website
// for details on labels.
func (t *Transcript) ExtractBytes(label, dest []byte) {
// for details on labels. If the length of label or dest will overflow
// a 32-bit unsigned integer this method will panic.
func (t *Transcript) ExtractBytes(dest []byte, label string) {
if lLen := uint64(len(label)); lLen > math.MaxUint32 {
panic("merlin: label length exceeds limits")
}
if dLen := uint64(len(dest)); dLen > math.MaxUint32 {
panic("merlin: dest length exceeds limits")
}

var sizeBuffer [4]byte
binary.LittleEndian.PutUint32(sizeBuffer[0:], uint32(len(dest)))

t.s.MetaAD(label, false)
t.s.MetaAD([]byte(label), false)
t.s.MetaAD(sizeBuffer[:], true)

t.s.PRF(dest)
Expand All @@ -106,14 +127,23 @@ type TranscriptRngBuilder struct {
s *strobe.Strobe
}

// RekeyWithWitnessBytes rekeys the transcript using the provided witness data.
func (rb *TranscriptRngBuilder) RekeyWithWitnessBytes(label, witness []byte) *TranscriptRngBuilder {
// RekeyWithitnessBytes rekeys the transcript using the provided witness data.
// If the length of label or witness will overflow a 32-bit unsigned
// integer this method will panic.
func (rb *TranscriptRngBuilder) RekeyWithWitnessBytes(label string, witness []byte) *TranscriptRngBuilder {
if lLen := uint64(len(label)); lLen > math.MaxUint32 {
panic("merlin: label length exceeds limits")
}
if wLen := uint64(len(witness)); wLen > math.MaxUint32 {
panic("merlin: witness length exceeds limits")
}

// AD[label || le32(len(witness))](witness)

var sizeBuffer [4]byte
binary.LittleEndian.PutUint32(sizeBuffer[0:], uint32(len(witness)))

rb.s.MetaAD(label, false)
rb.s.MetaAD([]byte(label), false)
rb.s.MetaAD(sizeBuffer[:], true)

rb.s.KEY(witness)
Expand All @@ -132,7 +162,7 @@ func (rb *TranscriptRngBuilder) Finalize(rng io.Reader) (io.Reader, error) {

randomBytes := make([]byte, 32)
if _, err := io.ReadFull(rng, randomBytes); err != nil {
return nil, fmt.Errorf("internal/merlin: failed to read entropy: %w", err)
return nil, fmt.Errorf("merlin: failed to read entropy: %w", err)
}

rb.s.MetaAD([]byte("rng"), false)
Expand All @@ -153,6 +183,9 @@ type transcriptRng struct {

func (rng *transcriptRng) Read(p []byte) (int, error) {
l := len(p)
if l > math.MaxUint32 {
return 0, fmt.Errorf("merlin: read length exceeds limits")
}

var sizeBuffer [4]byte
binary.LittleEndian.PutUint32(sizeBuffer[0:], uint32(l))
Expand Down
30 changes: 15 additions & 15 deletions internal/merlin/merlin_test.go → primitives/merlin/merlin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ import (

// testExtractBytes is a simple wrapper around ExtractBytes that allocates
// the destination buffer.
func (t *Transcript) testExtractBytes(label []byte, outLen int) []byte {
func (t *Transcript) testExtractBytes(label string, outLen int) []byte {
dest := make([]byte, outLen)
t.ExtractBytes(label, dest)
t.ExtractBytes(dest, label)
return dest
}

Expand All @@ -59,9 +59,9 @@ func (t *Transcript) testExtractBytes(label []byte, outLen int) []byte {

func TestSimpleTranscript(t *testing.T) {
mt := NewTranscript("test protocol")
mt.AppendMessage([]byte("some label"), []byte("some data"))
mt.AppendMessage("some label", []byte("some data"))

cBytes := mt.testExtractBytes([]byte("challenge"), 32)
cBytes := mt.testExtractBytes("challenge", 32)
cHex := fmt.Sprintf("%x", cBytes)
expectedHex := "d5a21972d0d5fe320c0d263fac7fffb8145aa640af6e9bca177c03c7efcf0615"

Expand All @@ -72,7 +72,7 @@ func TestSimpleTranscript(t *testing.T) {

func TestComplexTranscript(t *testing.T) {
tr := NewTranscript("test protocol")
tr.AppendMessage([]byte("step1"), []byte("some data"))
tr.AppendMessage("step1", []byte("some data"))

data := make([]byte, 1024)
for i := range data {
Expand All @@ -81,9 +81,9 @@ func TestComplexTranscript(t *testing.T) {

var chlBytes []byte
for i := 0; i < 32; i++ {
chlBytes = tr.testExtractBytes([]byte("challenge"), 32)
tr.AppendMessage([]byte("bigdata"), data)
tr.AppendMessage([]byte("challengedata"), chlBytes)
chlBytes = tr.testExtractBytes("challenge", 32)
tr.AppendMessage("bigdata", data)
tr.AppendMessage("challengedata", chlBytes)
}

expectedChlHex := "a8c933f54fae76e3f9bea93648c1308e7dfa2152dd51674ff3ca438351cf003c"
Expand All @@ -96,28 +96,28 @@ func TestComplexTranscript(t *testing.T) {

func TestClone(t *testing.T) {
mt := NewTranscript("test protocol")
mt.AppendMessage([]byte("some label"), []byte("some data"))
mt.AppendMessage("some label", []byte("some data"))

mtCopy, mtCopy2 := mt.Clone(), mt.Clone()

// Ensure that mtCopy matches what we would get from mt.
cBytes := mtCopy.testExtractBytes([]byte("challenge"), 32)
cBytes := mtCopy.testExtractBytes("challenge", 32)
cHex := fmt.Sprintf("%x", cBytes)
expectedHex := "d5a21972d0d5fe320c0d263fac7fffb8145aa640af6e9bca177c03c7efcf0615"
if cHex != expectedHex {
t.Errorf("\nmtCopy Got : %s\nWant: %s", cHex, expectedHex)
}

// Append more to mtCopy2, ensure that it is different.
mtCopy2.AppendMessage([]byte("someother label"), []byte("someother data"))
cBytes = mtCopy2.testExtractBytes([]byte("challenge"), 32)
mtCopy2.AppendMessage("someother label", []byte("someother data"))
cBytes = mtCopy2.testExtractBytes("challenge", 32)
cHex = fmt.Sprintf("%x", cBytes)
if cHex == expectedHex {
t.Errorf("\nmtCopy2 Got : %s\nWant: %s", cHex, expectedHex)
}

// Finally, extract from mt.
cBytes = mt.testExtractBytes([]byte("challenge"), 32)
cBytes = mt.testExtractBytes("challenge", 32)
cHex = fmt.Sprintf("%x", cBytes)
if cHex != expectedHex {
t.Errorf("\nmtCopy Got : %s\nWant: %s", cHex, expectedHex)
Expand All @@ -132,7 +132,7 @@ func TestTranscriptRng(t *testing.T) {
t3 := NewTranscript(protocolLabel)
t4 := NewTranscript(protocolLabel)

commitmentLabel := []byte("com")
commitmentLabel := "com"
commitment1 := []byte("commitment data 1")
commitment2 := []byte("commitment data 2")

Expand All @@ -141,7 +141,7 @@ func TestTranscriptRng(t *testing.T) {
t3.AppendMessage(commitmentLabel, commitment2)
t4.AppendMessage(commitmentLabel, commitment2)

witnessLabel := []byte("witness")
witnessLabel := "witness"
witness1 := []byte("witness data 1")
witness2 := []byte("witness data 2")

Expand Down
12 changes: 6 additions & 6 deletions primitives/sr25519/batch_verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ import (

"github.com/oasisprotocol/curve25519-voi/curve"
"github.com/oasisprotocol/curve25519-voi/curve/scalar"
"github.com/oasisprotocol/curve25519-voi/internal/merlin"
"github.com/oasisprotocol/curve25519-voi/internal/zeroreader"
"github.com/oasisprotocol/curve25519-voi/primitives/merlin"
)

type BatchVerifier struct {
Expand Down Expand Up @@ -86,7 +86,7 @@ func (e *entry) doInit(pk *PublicKey, transcript *SigningTranscript, signature *
e.hram.Set(deriveVerifyChallengeScalar(pk, transcript, signature))

// Calculate the transcript's delinearization component.
if err := transcript.witnessBytes([]byte{}, e.witnessBytes[:], nil, zeroreader.ZeroReader{}); err != nil {
if err := transcript.witnessBytes(e.witnessBytes[:], "", nil, zeroreader.ZeroReader{}); err != nil {
panic("sr25519: failed to generate transcript delinearization value: " + err.Error())
}
e.witnessA = pk.compressed
Expand Down Expand Up @@ -169,15 +169,15 @@ func (v *BatchVerifier) VerifyBatchOnly(rand io.Reader) bool {
t: merlin.NewTranscript("V-RNG"),
}
for i := range v.entries {
zs_t.commitPoint([]byte{}, &v.entries[i].witnessA)
zs_t.commitPoint("", &v.entries[i].witnessA)
}
for i := range v.entries {
zs_t.commitPoint([]byte{}, &v.entries[i].witnessR)
zs_t.commitPoint("", &v.entries[i].witnessR)
}
for i := range v.entries {
zs_t.commitBytes([]byte{}, v.entries[i].witnessBytes[:])
zs_t.commitBytes("", v.entries[i].witnessBytes[:])
}
zs_rng, err := zs_t.witnessRng([]byte{}, nil, rand)
zs_rng, err := zs_t.witnessRng("", nil, rand)
if err != nil {
panic("sr25519: failed to instantiate delinearization rng: " + err.Error())
}
Expand Down
1 change: 0 additions & 1 deletion primitives/sr25519/batch_verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
package sr25519

import (
//"crypto"
"fmt"
"testing"
)
Expand Down
Loading

0 comments on commit 9075215

Please sign in to comment.