Skip to content

Commit

Permalink
Create Hasher registry
Browse files Browse the repository at this point in the history
  • Loading branch information
vqhuy committed Jun 22, 2017
1 parent 9d040e8 commit 29aa6a2
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 124 deletions.
75 changes: 75 additions & 0 deletions crypto/hasher/coniks/coniks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// TODO(huyvq): Remove package import.
// Other package shouldn't need to import this package,
// instead they should insert `hasher` and import this
// package using a blank import name.
// Should be adressed in #06.

package coniks

import (
"crypto"

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

func init() {
hasher.RegisterHasher(CONIKSHasher, New)
}

const (
// CONIKSHasher is the identity of the hashing algorithm
// specified in the CONIKSHasher paper.
CONIKSHasher = "CONIKS Hasher"

emptyIdentifier = 'E'
leafIdentifier = 'L'
)

type coniksHasher struct {
crypto.Hash
}

// New returns an instance of CONIKS hasher.
func New() hasher.PADHasher {
return &coniksHasher{Hash: crypto.SHA512_256}
}

func (ch *coniksHasher) Digest(ms ...[]byte) []byte {
h := ch.New()
for _, m := range ms {
h.Write(m)
}
return h.Sum(nil)
}

func (coniksHasher) ID() string {
return CONIKSHasher
}

func (ch *coniksHasher) Size() int {
return ch.Size()
}

func (ch *coniksHasher) HashInterior(left, right []byte) []byte {
return ch.Digest(left, right)
}

func (ch *coniksHasher) HashLeaf(nonce []byte, index []byte, level uint32, commit []byte) []byte {
return ch.Digest(
[]byte{leafIdentifier},
nonce,
index,
utils.UInt32ToBytes(level),
commit,
)
}

func (ch *coniksHasher) HashEmpty(nonce []byte, index []byte, level uint32) []byte {
return ch.Digest(
[]byte{emptyIdentifier},
nonce,
index,
utils.UInt32ToBytes(level),
)
}
57 changes: 57 additions & 0 deletions crypto/hasher/coniks/coniks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package coniks

import (
"encoding/hex"
"testing"

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

// h2h converts a hex string into its Hash object.
func h2h(h string) hasher.Hash {
b, err := hex.DecodeString(h)
if err != nil {
panic("invalid hex string")
}
var ret hasher.Hash
copy(ret[:], b)
return ret
}

// s2h converts a byte slice into its Hash object.
func s2h(s []byte) hasher.Hash {
var ret hasher.Hash
copy(ret[:], s)
return ret
}

func TestHashLeafVectors(t *testing.T) {
for _, tc := range []struct {
treeNonce [32]byte // treeNonce is treeID in KT, it should be a 32-byte array for cross-project compatibility
index []byte
depth uint32
leaf []byte
want hasher.Hash
}{
{treeNonce: [32]byte{0}, index: []byte("foo"), depth: 128, leaf: []byte("leaf"), want: h2h("65e7f29787a6168affd016656bb1f4f03af91cf7416270f5015005f8594d3eb6")},
} {
if got, want := s2h(New().HashLeaf(tc.treeNonce[:], tc.index, tc.depth, tc.leaf)), tc.want; got != want {
t.Errorf("HashLeaf(%v, %s, %v, %s): %x, want %x", tc.treeNonce, tc.index, tc.depth, tc.leaf, got, want)
}
}
}

func TestHashEmptyVectors(t *testing.T) {
for _, tc := range []struct {
treeNonce [32]byte // treeNonce is treeID in KT, it should be a 32-byte array for cross-project compatibility
index []byte
depth uint32
want hasher.Hash
}{
{treeNonce: [32]byte{0}, index: []byte("foo"), depth: 128, want: h2h("1a6b0eb739b32a46e7d679a9be03f522e907f53423aacb82e550bf657d1afb10")},
} {
if got, want := s2h(New().HashEmpty(tc.treeNonce[:], tc.index, tc.depth)), tc.want; got != want {
t.Errorf("HashLeaf(%v, %s, %v): %x, want %x", tc.treeNonce, tc.index, tc.depth, got, want)
}
}
}
97 changes: 28 additions & 69 deletions crypto/hasher/hasher.go
Original file line number Diff line number Diff line change
@@ -1,94 +1,53 @@
package hasher

import (
"crypto"
"fmt"

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

const (
emptyIdentifier = 'E'
leafIdentifier = 'L'
"github.com/coniks-sys/coniks-go/crypto"
)

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

// PADHasher provides hash functions for the PAD implementations.
type PADHasher interface {
// ID returns the name of the cryptographic hash function.
ID() string
// Size returns the size of the hash output in bytes.
Size() int
// Digest hashes all passed byte slices. The passed slices won't be mutated.
Digest(ms ...[]byte) []byte
TreeHasher
treeHasher
}

// TreeHasher provides hash functions for tree implementations.
type TreeHasher interface {
// treeHasher provides hash functions for tree implementations.
type treeHasher interface {
// HashInterior computes the hash of an interior node as: H(left || right)
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
}
// HashLeaf computes the hash of a user leaf node as:
// H(Identifier || nonce || index || level || commit)
HashLeaf(nonce []byte, index []byte, level uint32, data []byte) []byte

// New creates a new PADHasher using the passed in hash function.
func New(h crypto.Hash) PADHasher {
return &coniksHasher{Hash: h}
// HashEmpty computes the hash of an empty leaf node as:
// H(Identifier || nonce || index || level)
HashEmpty(nonce []byte, index []byte, level uint32) []byte
}

// Default is the standard CONIKS hasher.
func Default() PADHasher {
return New(crypto.SHA512_256)
}
var hashers = make(map[string]PADHasher)

// 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)
// RegisterHasher registers a hasher for use.
func RegisterHasher(h string, f func() PADHasher) {
if _, ok := hashers[h]; ok {
panic(fmt.Sprintf("RegisterHasher(%v) is already registered", h))
}
return h.Sum(nil)
hashers[h] = f()
}

// 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),
)
// Hasher returns a PADHasher.
func Hasher(h string) (PADHasher, error) {
if f, ok := hashers[h]; ok {
return f, nil
}
return nil, fmt.Errorf("Hasher(%v) is unknown hasher", h)
}
59 changes: 18 additions & 41 deletions crypto/hasher/hasher_test.go
Original file line number Diff line number Diff line change
@@ -1,55 +1,32 @@
package hasher

import (
"encoding/hex"
"testing"
)

// h2h converts a hex string into its Hash object.
func h2h(h string) Hash {
b, err := hex.DecodeString(h)
if err != nil {
panic("invalid hex string")
}
var ret Hash
copy(ret[:], b)
return ret
}
var fakeHasherID = "fakeHasher"

// s2h converts a byte slice into its Hash object.
func s2h(s []byte) Hash {
var ret Hash
copy(ret[:], s)
return ret
func fakeHasher() PADHasher {
return nil
}

func TestHashLeafVectors(t *testing.T) {
for _, tc := range []struct {
treeNonce [32]byte // treeNonce is treeID in KT, it should be a 32-byte array for cross-project compatibility
index []byte
depth uint32
leaf []byte
want Hash
}{
{treeNonce: [32]byte{0}, index: []byte("foo"), depth: 128, leaf: []byte("leaf"), want: h2h("65e7f29787a6168affd016656bb1f4f03af91cf7416270f5015005f8594d3eb6")},
} {
if got, want := s2h(Default().HashLeaf(tc.treeNonce[:], tc.index, tc.depth, tc.leaf)), tc.want; got != want {
t.Errorf("HashLeaf(%v, %s, %v, %s): %x, want %x", tc.treeNonce, tc.index, tc.depth, tc.leaf, got, want)
func TestHasherIsRegistered(t *testing.T) {
RegisterHasher(fakeHasherID, fakeHasher)
defer func() {
if r := recover(); r == nil {
t.Fatal("Expected RegisterHasher to panic.")
}
}
}()
RegisterHasher(fakeHasherID, fakeHasher)
}

func TestHashEmptyVectors(t *testing.T) {
for _, tc := range []struct {
treeNonce [32]byte // treeNonce is treeID in KT, it should be a 32-byte array for cross-project compatibility
index []byte
depth uint32
want Hash
}{
{treeNonce: [32]byte{0}, index: []byte("foo"), depth: 128, want: h2h("1a6b0eb739b32a46e7d679a9be03f522e907f53423aacb82e550bf657d1afb10")},
} {
if got, want := s2h(Default().HashEmpty(tc.treeNonce[:], tc.index, tc.depth)), tc.want; got != want {
t.Errorf("HashLeaf(%v, %s, %v): %x, want %x", tc.treeNonce, tc.index, tc.depth, got, want)
}
func TestGetHasher(t *testing.T) {
if _, ok := hashers[fakeHasherID]; !ok {
RegisterHasher(fakeHasherID, fakeHasher)
}

_, err := Hasher(fakeHasherID)
if err != nil {
t.Error("Expect a hasher.")
}
}
8 changes: 4 additions & 4 deletions merkletree/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package merkletree

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

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

func (n *userLeafNode) hash(m *MerkleTree) []byte {
return hasher.Default().HashLeaf(
return conikshasher.New().HashLeaf(
m.nonce,
n.index,
n.level,
Expand All @@ -96,7 +96,7 @@ func (n *userLeafNode) hash(m *MerkleTree) []byte {
}

func (n *emptyNode) hash(m *MerkleTree) []byte {
return hasher.Default().HashEmpty(
return conikshasher.New().HashEmpty(
m.nonce,
n.index,
n.level,
Expand Down
4 changes: 2 additions & 2 deletions merkletree/pad.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"errors"

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

0 comments on commit 29aa6a2

Please sign in to comment.