-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
193 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.