Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Expose personalization, salt, tree hashing #1

Merged
merged 2 commits into from
Aug 20, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions blake2.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ extern "C" {

int blake2b_init( blake2b_state *S, const uint8_t outlen );
int blake2b_init_key( blake2b_state *S, const uint8_t outlen, const void *key, const uint8_t keylen );
int blake2b_init_parametrized( blake2b_state *S, const blake2b_param *P, const void *key );
int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
int blake2b_update( blake2b_state *S, const uint8_t *in, uint64_t inlen );
int blake2b_final( blake2b_state *S, uint8_t *out, uint8_t outlen );
Expand Down
21 changes: 21 additions & 0 deletions blake2b.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,27 @@ int blake2b_init_key( blake2b_state *S, const uint8_t outlen, const void *key, c
return 0;
}

int blake2b_init_parametrized( blake2b_state *S, const blake2b_param *P, const void *key )
{
if ( ( !P->digest_length ) || ( P->digest_length > BLAKE2B_OUTBYTES ) ) return -1;

if ( P->key_length > BLAKE2B_KEYBYTES ) return -1;

if( blake2b_init_param( S, P ) < 0 )
return 0;

if (P->key_length > 0)
{
uint8_t block[BLAKE2B_BLOCKBYTES];
memset( block, 0, BLAKE2B_BLOCKBYTES );
memcpy( block, key, P->key_length );
blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
}
return 0;
}


static inline int blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
{
__m128i row1l, row1h;
Expand Down
135 changes: 111 additions & 24 deletions blake2b.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,136 @@ import (
)

type digest struct {
state *C.blake2b_state
key []byte
size int
state *C.blake2b_state
key []byte
param C.blake2b_param
isLastNode bool
}

// NewBlake2B returns a new 512-bit BLAKE2B hash.
func NewBlake2B() hash.Hash {
d := new(digest)
d.size = 64
const (
SaltSize = C.BLAKE2B_SALTBYTES
PersonalSize = C.BLAKE2B_PERSONALBYTES
)

// Tree contains parameters for tree hashing. Each node in the tree
// can be hashed concurrently, and incremental changes can be done in
// a Merkle tree fashion.
type Tree struct {
// Fanout: how many children each tree node has. 0 for unlimited.
// 1 means sequential mode.
Fanout uint8
// Maximal depth of the tree. Beyond this height, nodes are just
// added to the root of the tree. 255 for unlimited. 1 means
// sequential mode.
MaxDepth uint8
// Leaf maximal byte length, how much data each leaf summarizes. 0
// for unlimited or sequential mode.
LeafSize uint32
// Depth of this node. 0 for leaves or sequential mode.
NodeDepth uint8
// Offset of this node within this level of the tree. 0 for the
// first, leftmost, leaf, or sequential mode.
NodeOffset uint64
// Inner hash byte length, in the range [0, 64]. 0 for sequential
// mode.
InnerHashSize uint8

// IsLastNode indicates this node is the last, rightmost, node of
// a level of the tree.
IsLastNode bool
}

// Config contains parameters for the hash function that affect its
// output.
type Config struct {
// Digest byte length, in the range [1, 64]. If 0, default size of 64 bytes is used.
Size uint8
// Key is up to 64 arbitrary bytes, for keyed hashing mode. Can be nil.
Key []byte
// Salt is up to 16 arbitrary bytes, used to randomize the hash. Can be nil.
Salt []byte
// Personal is up to 16 arbitrary bytes, used to make the hash
// function unique for each application. Can be nil.
Personal []byte

// Parameters for tree hashing. Set to nil to use default
// sequential mode.
Tree *Tree
}

// New returns a new custom BLAKE2b hash.
//
// If config is nil, uses a 64-byte digest size.
func New(config *Config) hash.Hash {
d := &digest{
param: C.blake2b_param{
digest_length: 64,
fanout: 1,
depth: 1,
},
}
if config != nil {
if config.Size != 0 {
d.param.digest_length = C.uint8_t(config.Size)
}
if len(config.Key) > 0 {
// let the C library worry about the exact limit; we just
// worry about fitting into the variable
if len(config.Key) > 255 {
panic("blake2b key too long")
}
d.param.key_length = C.uint8_t(len(config.Key))
d.key = config.Key
}
salt := (*[C.BLAKE2B_SALTBYTES]byte)(unsafe.Pointer(&d.param.salt[0]))
copy(salt[:], config.Salt)
personal := (*[C.BLAKE2B_SALTBYTES]byte)(unsafe.Pointer(&d.param.personal[0]))
copy(personal[:], config.Personal)

if config.Tree != nil {
d.param.fanout = C.uint8_t(config.Tree.Fanout)
d.param.depth = C.uint8_t(config.Tree.MaxDepth)
d.param.leaf_length = C.uint32_t(config.Tree.LeafSize)
d.param.node_offset = C.uint64_t(config.Tree.NodeOffset)
d.param.node_depth = C.uint8_t(config.Tree.NodeDepth)
d.param.inner_length = C.uint8_t(config.Tree.InnerHashSize)

d.isLastNode = config.Tree.IsLastNode
}
}
d.Reset()
return d
}

// NewBlake2B returns a new 512-bit BLAKE2B hash.
func NewBlake2B() hash.Hash {
return New(&Config{Size: 64})
}

// NewKeyedBlake2B returns a new 512-bit BLAKE2B hash with the given secret key.
func NewKeyedBlake2B(key []byte) hash.Hash {
d := new(digest)
d.size = 64
d.key = key
d.Reset()
return d
return New(&Config{Size: 64, Key: key})
}

func (*digest) BlockSize() int {
return 128
}

func (d *digest) Size() int {
return d.size
return int(d.param.digest_length)
}

func (d *digest) Reset() {
d.state = new(C.blake2b_state)
l := C.uint8_t(d.Size())
if len(d.key) == 0 {
if C.blake2b_init(d.state, l) < 0 {
panic("blake2: unable to reset")
}
} else {
key := unsafe.Pointer(&d.key[0])
keyLen := C.uint8_t(len(d.key))
if C.blake2b_init_key(d.state, l, key, keyLen) < 0 {
panic("blake2: unable to reset")
}
var key unsafe.Pointer
if d.param.key_length > 0 {
key = unsafe.Pointer(&d.key[0])
}
if C.blake2b_init_parametrized(d.state, &d.param, key) < 0 {
panic("blake2: unable to reset")
}
if d.isLastNode {
d.state.last_node = C.uint8_t(1)
}
}

Expand Down
50 changes: 50 additions & 0 deletions blake2b_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,56 @@ func TestBlake2B(t *testing.T) {
}
}

func ExampleNew() {
h := New(nil)
h.Write([]byte("one two three"))
d := h.Sum(nil)
fmt.Printf("%X", d)
// Output:
// 5BD2901E0955770DE513E3C2397F3B7594E6BCF21F61708DF64AAECCD6BC2BE6EAE0A2CA524CCB2A7F054464B07472B9E130966D3CE4B1870E02DA788C4E33BE
}

func ExampleNew_short() {
h := New(&Config{Size: 32})
h.Write([]byte("one two three"))
d := h.Sum(nil)
fmt.Printf("%X", d)
// Output:
// 4BBA13CA5E6C7347347A331F69CCB09872E873E9FB415A2387B025712F68844B
}

func ExampleNew_personalized() {
h := New(&Config{
Key: []byte("sekrit"),
Salt: []byte("random but public"),
Personal: []byte("myAppName"),
})
h.Write([]byte("one two three"))
d := h.Sum(nil)
fmt.Printf("%X", d)
// Output:
// FE995AB57D24D0A0DB081BB8C1E1EB69F46A3CE5AC97FD463837C5FCAA18080688C7389449692F32D6FD53C6B7A475E52AFF5C3FBDCD253715C4F8D1333068C5
}

func ExampleNew_treehash() {
h := New(&Config{
Tree: &Tree{
Fanout: 64,
MaxDepth: 8,
LeafSize: 65536,
InnerHashSize: 32,
NodeDepth: 3,
NodeOffset: 23,
IsLastNode: true,
},
})
h.Write([]byte("one two three"))
d := h.Sum(nil)
fmt.Printf("%X", d)
// Output:
// E86CF85D23FF3E33CCBC37F37B3A8EAE0FAE26E763FB5253F3D740DF823D47AB1273D6FFC53AD8FB15F3153F3E9F92974510975AE08ED311C68D3E4C0A3B21A6
}

func ExampleNewBlake2B() {
h := NewBlake2B()
h.Write([]byte("one two three"))
Expand Down