package blake2b
import (
"encoding/binary"
"errors"
"hash"
)
const (
BlockSize = 128
Size = 64
Size384 = 48
Size256 = 32
)
var (
useAVX2 bool
useAVX bool
useSSE4 bool
)
var (
errKeySize = errors .New ("blake2b: invalid key size" )
errHashSize = errors .New ("blake2b: invalid hash size" )
)
var iv = [8 ]uint64 {
0x6a09e667f3bcc908 , 0xbb67ae8584caa73b , 0x3c6ef372fe94f82b , 0xa54ff53a5f1d36f1 ,
0x510e527fade682d1 , 0x9b05688c2b3e6c1f , 0x1f83d9abfb41bd6b , 0x5be0cd19137e2179 ,
}
func Sum512 (data []byte ) [Size ]byte {
var sum [Size ]byte
checkSum (&sum , Size , data )
return sum
}
func Sum384 (data []byte ) [Size384 ]byte {
var sum [Size ]byte
var sum384 [Size384 ]byte
checkSum (&sum , Size384 , data )
copy (sum384 [:], sum [:Size384 ])
return sum384
}
func Sum256 (data []byte ) [Size256 ]byte {
var sum [Size ]byte
var sum256 [Size256 ]byte
checkSum (&sum , Size256 , data )
copy (sum256 [:], sum [:Size256 ])
return sum256
}
func New512 (key []byte ) (hash .Hash , error ) { return newDigest (Size , key ) }
func New384 (key []byte ) (hash .Hash , error ) { return newDigest (Size384 , key ) }
func New256 (key []byte ) (hash .Hash , error ) { return newDigest (Size256 , key ) }
func New (size int , key []byte ) (hash .Hash , error ) { return newDigest (size , key ) }
func newDigest (hashSize int , key []byte ) (*digest , error ) {
if hashSize < 1 || hashSize > Size {
return nil , errHashSize
}
if len (key ) > Size {
return nil , errKeySize
}
d := &digest {
size : hashSize ,
keyLen : len (key ),
}
copy (d .key [:], key )
d .Reset ()
return d , nil
}
func checkSum (sum *[Size ]byte , hashSize int , data []byte ) {
h := iv
h [0 ] ^= uint64 (hashSize ) | (1 << 16 ) | (1 << 24 )
var c [2 ]uint64
if length := len (data ); length > BlockSize {
n := length &^ (BlockSize - 1 )
if length == n {
n -= BlockSize
}
hashBlocks (&h , &c , 0 , data [:n ])
data = data [n :]
}
var block [BlockSize ]byte
offset := copy (block [:], data )
remaining := uint64 (BlockSize - offset )
if c [0 ] < remaining {
c [1 ]--
}
c [0 ] -= remaining
hashBlocks (&h , &c , 0xFFFFFFFFFFFFFFFF , block [:])
for i , v := range h [:(hashSize +7 )/8 ] {
binary .LittleEndian .PutUint64 (sum [8 *i :], v )
}
}
type digest struct {
h [8 ]uint64
c [2 ]uint64
size int
block [BlockSize ]byte
offset int
key [BlockSize ]byte
keyLen int
}
const (
magic = "b2b"
marshaledSize = len (magic ) + 8 *8 + 2 *8 + 1 + BlockSize + 1
)
func (d *digest ) MarshalBinary () ([]byte , error ) {
if d .keyLen != 0 {
return nil , errors .New ("crypto/blake2b: cannot marshal MACs" )
}
b := make ([]byte , 0 , marshaledSize )
b = append (b , magic ...)
for i := 0 ; i < 8 ; i ++ {
b = appendUint64 (b , d .h [i ])
}
b = appendUint64 (b , d .c [0 ])
b = appendUint64 (b , d .c [1 ])
b = append (b , byte (d .size ))
b = append (b , d .block [:]...)
b = append (b , byte (d .offset ))
return b , nil
}
func (d *digest ) UnmarshalBinary (b []byte ) error {
if len (b ) < len (magic ) || string (b [:len (magic )]) != magic {
return errors .New ("crypto/blake2b: invalid hash state identifier" )
}
if len (b ) != marshaledSize {
return errors .New ("crypto/blake2b: invalid hash state size" )
}
b = b [len (magic ):]
for i := 0 ; i < 8 ; i ++ {
b , d .h [i ] = consumeUint64 (b )
}
b , d .c [0 ] = consumeUint64 (b )
b , d .c [1 ] = consumeUint64 (b )
d .size = int (b [0 ])
b = b [1 :]
copy (d .block [:], b [:BlockSize ])
b = b [BlockSize :]
d .offset = int (b [0 ])
return nil
}
func (d *digest ) BlockSize () int { return BlockSize }
func (d *digest ) Size () int { return d .size }
func (d *digest ) Reset () {
d .h = iv
d .h [0 ] ^= uint64 (d .size ) | (uint64 (d .keyLen ) << 8 ) | (1 << 16 ) | (1 << 24 )
d .offset , d .c [0 ], d .c [1 ] = 0 , 0 , 0
if d .keyLen > 0 {
d .block = d .key
d .offset = BlockSize
}
}
func (d *digest ) Write (p []byte ) (n int , err error ) {
n = len (p )
if d .offset > 0 {
remaining := BlockSize - d .offset
if n <= remaining {
d .offset += copy (d .block [d .offset :], p )
return
}
copy (d .block [d .offset :], p [:remaining ])
hashBlocks (&d .h , &d .c , 0 , d .block [:])
d .offset = 0
p = p [remaining :]
}
if length := len (p ); length > BlockSize {
nn := length &^ (BlockSize - 1 )
if length == nn {
nn -= BlockSize
}
hashBlocks (&d .h , &d .c , 0 , p [:nn ])
p = p [nn :]
}
if len (p ) > 0 {
d .offset += copy (d .block [:], p )
}
return
}
func (d *digest ) Sum (sum []byte ) []byte {
var hash [Size ]byte
d .finalize (&hash )
return append (sum , hash [:d .size ]...)
}
func (d *digest ) finalize (hash *[Size ]byte ) {
var block [BlockSize ]byte
copy (block [:], d .block [:d .offset ])
remaining := uint64 (BlockSize - d .offset )
c := d .c
if c [0 ] < remaining {
c [1 ]--
}
c [0 ] -= remaining
h := d .h
hashBlocks (&h , &c , 0xFFFFFFFFFFFFFFFF , block [:])
for i , v := range h {
binary .LittleEndian .PutUint64 (hash [8 *i :], v )
}
}
func appendUint64 (b []byte , x uint64 ) []byte {
var a [8 ]byte
binary .BigEndian .PutUint64 (a [:], x )
return append (b , a [:]...)
}
func appendUint32 (b []byte , x uint32 ) []byte {
var a [4 ]byte
binary .BigEndian .PutUint32 (a [:], x )
return append (b , a [:]...)
}
func consumeUint64 (b []byte ) ([]byte , uint64 ) {
x := binary .BigEndian .Uint64 (b )
return b [8 :], x
}
func consumeUint32 (b []byte ) ([]byte , uint32 ) {
x := binary .BigEndian .Uint32 (b )
return b [4 :], x
}