Skip to content

Commit

Permalink
Create PADHasher & TreeHasher interface to allow developers to use th…
Browse files Browse the repository at this point in the history
…eir own hash function.

* Convert the default hash function to SHA-512/256

* Part of #6

Credit to KT's team
  • Loading branch information
vqhuy committed Jun 20, 2017
1 parent e9c8209 commit 65f9ddb
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 107 deletions.
92 changes: 92 additions & 0 deletions crypto/hasher/hasher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package hasher

import (
"crypto"

ccrypto "github.com/coniks-sys/coniks-go/crypto"
"github.com/coniks-sys/coniks-go/utils"
)

const (
emptyIdentifier = 'E'
leafIdentifier = 'L'
)

// Hash represents the output of the used hash function.
type Hash [ccrypto.DefaultHashSizeByte]byte

// Default is the standard CONIKS hasher.
var Default = New(crypto.SHA512_256)

// PADHasher provides hash functions for the PAD implementations.
type PADHasher interface {
Digest(ms ...[]byte) []byte
TreeHasher
}

// TreeHasher provides hash functions for tree implementations.
type TreeHasher interface {
ID() string
Size() int
HashInterior(left, right []byte) []byte
HashLeaf(nonce []byte, index []byte, level uint32, data []byte) []byte
HashEmpty(nonce []byte, index []byte, level uint32) []byte
}

type coniksHasher struct {
crypto.Hash
}

// New creates a new PADHasher using the passed in hash function.
func New(h crypto.Hash) PADHasher {
return &coniksHasher{Hash: h}
}

// Digest hashes all passed byte slices.
// The passed slices won't be mutated.
func (ch *coniksHasher) Digest(ms ...[]byte) []byte {
h := ch.New()
for _, m := range ms {
h.Write(m)
}
return h.Sum(nil)
}

// ID returns the name of the cryptographic hash function in string.
func (coniksHasher) ID() string {
return "SHA-512/256"
}

// Size returns the size of the hash output in bytes.
func (ch *coniksHasher) Size() int {
return ch.Size()
}

// HashInterior computes the hash of an interior node:
// H(left || right)
func (ch *coniksHasher) HashInterior(left, right []byte) []byte {
return ch.Digest(left, right)
}

// HashLeaf computes the hash of a user leaf node:
// H(Identifier || nonce || index || level || commit)
func (ch *coniksHasher) HashLeaf(nonce []byte, index []byte, level uint32, commit []byte) []byte {
return ch.Digest(
[]byte{leafIdentifier},
nonce,
index,
utils.UInt32ToBytes(level),
commit,
)
}

// HashEmpty computes the hash of an empty leaf node:
// H(Identifier || nonce || index || level)
func (ch *coniksHasher) HashEmpty(nonce []byte, index []byte, level uint32) []byte {
return ch.Digest(
[]byte{emptyIdentifier},
nonce,
index,
utils.UInt32ToBytes(level),
)
}
28 changes: 11 additions & 17 deletions crypto/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,20 @@ import (
"bytes"
"crypto/rand"

"golang.org/x/crypto/sha3"
"crypto/sha512"
)

const (
// HashSizeByte is the size of the hash output in bytes.
HashSizeByte = 32
// HashID identifies the used hash as a string.
HashID = "SHAKE128"
)
// DefaultHashSizeByte is the size of the hash function in bytes.
const DefaultHashSizeByte = sha512.Size256

// Digest hashes all passed byte slices.
// digest hashes all passed byte slices.
// The passed slices won't be mutated.
func Digest(ms ...[]byte) []byte {
h := sha3.NewShake128()
func digest(ms ...[]byte) []byte {
h := sha512.New512_256()
for _, m := range ms {
h.Write(m)
}
ret := make([]byte, HashSizeByte)
h.Read(ret)
return ret
return h.Sum(nil)
}

// MakeRand returns a random slice of bytes.
Expand All @@ -36,12 +30,12 @@ func Digest(ms ...[]byte) []byte {
// as unpredictable as desired).
// See https://trac.torproject.org/projects/tor/ticket/17694
func MakeRand() ([]byte, error) {
r := make([]byte, HashSizeByte)
r := make([]byte, DefaultHashSizeByte)
if _, err := rand.Read(r); err != nil {
return nil, err
}
// Do not directly reveal bytes from rand.Read on the wire
return Digest(r), nil
return digest(r), nil
}

// Commit can be used to create a cryptographic commit to some value (use
Expand All @@ -64,12 +58,12 @@ func NewCommit(stuff ...[]byte) (*Commit, error) {
}
return &Commit{
Salt: salt,
Value: Digest(append([][]byte{salt}, stuff...)...),
Value: digest(append([][]byte{salt}, stuff...)...),
}, nil
}

// Verify verifies that the underlying commit c was a commit to the passed
// byte slices stuff (which won't be mutated).
func (c *Commit) Verify(stuff ...[]byte) bool {
return bytes.Equal(c.Value, Digest(append([][]byte{c.Salt}, stuff...)...))
return bytes.Equal(c.Value, digest(append([][]byte{c.Salt}, stuff...)...))
}
16 changes: 2 additions & 14 deletions crypto/util_test.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,15 @@
package crypto

import (
"bytes"
"crypto/rand"
"errors"
"testing"
)

func TestDigest(t *testing.T) {
msg := []byte("test message")
d := Digest(msg)
if len(d) != HashSizeByte {
t.Fatal("Computation of Hash failed.")
}
if bytes.Equal(d, make([]byte, HashSizeByte)) {
t.Fatal("Hash is all zeros.")
}
}

type testErrorRandReader struct{}

func (er testErrorRandReader) Read([]byte) (int, error) {
return 0, errors.New("Not enough entropy!")
return 0, errors.New("not enough entropy")
}

func TestMakeRand(t *testing.T) {
Expand All @@ -30,7 +18,7 @@ func TestMakeRand(t *testing.T) {
t.Fatal(err)
}
// check if hashed the random output:
if len(r) != HashSizeByte {
if len(r) != DefaultHashSizeByte {
t.Fatal("Looks like Digest wasn't called correctly.")
}
orig := rand.Reader
Expand Down
3 changes: 2 additions & 1 deletion merkletree/merkletree.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"

"github.com/coniks-sys/coniks-go/crypto"
"github.com/coniks-sys/coniks-go/crypto/hasher"
"github.com/coniks-sys/coniks-go/utils"
)

Expand Down Expand Up @@ -72,7 +73,7 @@ func (m *MerkleTree) Get(lookupIndex []byte) *AuthenticationPath {
break
}
direction := lookupIndexBits[depth]
var hashArr [crypto.HashSizeByte]byte
var hashArr hasher.Hash
if direction {
copy(hashArr[:], nodePointer.(*interiorNode).leftHash)
nodePointer = nodePointer.(*interiorNode).rightChild
Expand Down
43 changes: 0 additions & 43 deletions merkletree/merkletree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"testing"

"github.com/coniks-sys/coniks-go/crypto/vrf"
"github.com/coniks-sys/coniks-go/utils"
"golang.org/x/crypto/sha3"
)

var vrfPrivKey1, _ = vrf.GenerateKey(bytes.NewReader(
Expand All @@ -21,9 +19,6 @@ func TestOneEntry(t *testing.T) {
t.Fatal(err)
}

var commit [32]byte
var expect [32]byte

key := "key"
val := []byte("value")
index := vrfPrivKey1.Compute([]byte(key))
Expand All @@ -32,50 +27,12 @@ func TestOneEntry(t *testing.T) {
}
m.recomputeHash()

// Check empty node hash
h := sha3.NewShake128()
h.Write([]byte{EmptyBranchIdentifier})
h.Write(m.nonce)
h.Write(utils.ToBytes([]bool{true}))
h.Write(utils.UInt32ToBytes(1))
h.Read(expect[:])
if !bytes.Equal(m.root.rightHash, expect[:]) {
t.Error("Wrong righ hash!",
"expected", expect,
"get", m.root.rightHash)
}

r := m.Get(index)
if r.Leaf.Value == nil {
t.Error("Cannot find value of key:", key)
return
}
v := r.Leaf.Value
if !bytes.Equal(v, val) {
t.Errorf("Value mismatch %v / %v", v, val)
}

// Check leaf node hash
h.Reset()
h.Write(r.Leaf.Commitment.Salt)
h.Write([]byte(key))
h.Write(val)
h.Read(commit[:])

h.Reset()
h.Write([]byte{LeafIdentifier})
h.Write(m.nonce)
h.Write(index)
h.Write(utils.UInt32ToBytes(1))
h.Write(commit[:])
h.Read(expect[:])

if !bytes.Equal(m.root.leftHash, expect[:]) {
t.Error("Wrong left hash!",
"expected", expect,
"get", m.root.leftHash)
}

r = m.Get([]byte("abc"))
if r.Leaf.Value != nil {
t.Error("Invalid look-up operation:", key)
Expand Down
23 changes: 11 additions & 12 deletions merkletree/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package merkletree

import (
"github.com/coniks-sys/coniks-go/crypto"
"github.com/coniks-sys/coniks-go/crypto/hasher"
"github.com/coniks-sys/coniks-go/utils"
)

Expand Down Expand Up @@ -82,25 +83,23 @@ func (n *interiorNode) hash(m *MerkleTree) []byte {
if n.rightHash == nil {
n.rightHash = n.rightChild.hash(m)
}
return crypto.Digest(n.leftHash, n.rightHash)
return hasher.Default.HashInterior(n.leftHash, n.rightHash)
}

func (n *userLeafNode) hash(m *MerkleTree) []byte {
return crypto.Digest(
[]byte{LeafIdentifier}, // K_leaf
[]byte(m.nonce), // K_n
[]byte(n.index), // i
[]byte(utils.UInt32ToBytes(n.level)), // l
[]byte(n.commitment.Value), // commit(key|| value)
return hasher.Default.HashLeaf(
m.nonce,
n.index,
n.level,
n.commitment.Value,
)
}

func (n *emptyNode) hash(m *MerkleTree) []byte {
return crypto.Digest(
[]byte{EmptyBranchIdentifier}, // K_empty
[]byte(m.nonce), // K_n
[]byte(n.index), // i
[]byte(utils.UInt32ToBytes(n.level)), // l
return hasher.Default.HashEmpty(
m.nonce,
n.index,
n.level,
)
}

Expand Down
3 changes: 2 additions & 1 deletion merkletree/pad.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"

"github.com/coniks-sys/coniks-go/crypto"
"github.com/coniks-sys/coniks-go/crypto/hasher"
"github.com/coniks-sys/coniks-go/crypto/sign"
"github.com/coniks-sys/coniks-go/crypto/vrf"
)
Expand Down Expand Up @@ -63,7 +64,7 @@ func (pad *PAD) signTreeRoot(epoch uint64) {
panic(err)
}
} else {
prevHash = crypto.Digest(pad.latestSTR.Signature)
prevHash = hasher.Default.Digest(pad.latestSTR.Signature)
}
pad.tree.recomputeHash()
m := pad.tree.Clone()
Expand Down
Loading

0 comments on commit 65f9ddb

Please sign in to comment.