1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package blake2b implements the BLAKE2b hash algorithm defined by RFC 7693
6// and the extendable output function (XOF) BLAKE2Xb.
7//
8// For a detailed specification of BLAKE2b see https://blake2.net/blake2.pdf
9// and for BLAKE2Xb see https://blake2.net/blake2x.pdf
10//
11// If you aren't sure which function you need, use BLAKE2b (Sum512 or New512).
12// If you need a secret-key MAC (message authentication code), use the New512
13// function with a non-nil key.
14//
15// BLAKE2X is a construction to compute hash values larger than 64 bytes. It
16// can produce hash values between 0 and 4 GiB.
17package blake2b
18
19import (
20	"encoding/binary"
21	"errors"
22	"hash"
23)
24
25const (
26	// The blocksize of BLAKE2b in bytes.
27	BlockSize = 128
28	// The hash size of BLAKE2b-512 in bytes.
29	Size = 64
30	// The hash size of BLAKE2b-384 in bytes.
31	Size384 = 48
32	// The hash size of BLAKE2b-256 in bytes.
33	Size256 = 32
34)
35
36var (
37	useAVX2 bool
38	useAVX  bool
39	useSSE4 bool
40)
41
42var errKeySize = errors.New("blake2b: invalid key size")
43
44var iv = [8]uint64{
45	0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
46	0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
47}
48
49// Sum512 returns the BLAKE2b-512 checksum of the data.
50func Sum512(data []byte) [Size]byte {
51	var sum [Size]byte
52	checkSum(&sum, Size, data)
53	return sum
54}
55
56// Sum384 returns the BLAKE2b-384 checksum of the data.
57func Sum384(data []byte) [Size384]byte {
58	var sum [Size]byte
59	var sum384 [Size384]byte
60	checkSum(&sum, Size384, data)
61	copy(sum384[:], sum[:Size384])
62	return sum384
63}
64
65// Sum256 returns the BLAKE2b-256 checksum of the data.
66func Sum256(data []byte) [Size256]byte {
67	var sum [Size]byte
68	var sum256 [Size256]byte
69	checkSum(&sum, Size256, data)
70	copy(sum256[:], sum[:Size256])
71	return sum256
72}
73
74// New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil
75// key turns the hash into a MAC. The key must between zero and 64 bytes long.
76func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
77
78// New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil
79// key turns the hash into a MAC. The key must between zero and 64 bytes long.
80func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
81
82// New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil
83// key turns the hash into a MAC. The key must between zero and 64 bytes long.
84func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
85
86func newDigest(hashSize int, key []byte) (*digest, error) {
87	if len(key) > Size {
88		return nil, errKeySize
89	}
90	d := &digest{
91		size:   hashSize,
92		keyLen: len(key),
93	}
94	copy(d.key[:], key)
95	d.Reset()
96	return d, nil
97}
98
99func checkSum(sum *[Size]byte, hashSize int, data []byte) {
100	h := iv
101	h[0] ^= uint64(hashSize) | (1 << 16) | (1 << 24)
102	var c [2]uint64
103
104	if length := len(data); length > BlockSize {
105		n := length &^ (BlockSize - 1)
106		if length == n {
107			n -= BlockSize
108		}
109		hashBlocks(&h, &c, 0, data[:n])
110		data = data[n:]
111	}
112
113	var block [BlockSize]byte
114	offset := copy(block[:], data)
115	remaining := uint64(BlockSize - offset)
116	if c[0] < remaining {
117		c[1]--
118	}
119	c[0] -= remaining
120
121	hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
122
123	for i, v := range h[:(hashSize+7)/8] {
124		binary.LittleEndian.PutUint64(sum[8*i:], v)
125	}
126}
127
128type digest struct {
129	h      [8]uint64
130	c      [2]uint64
131	size   int
132	block  [BlockSize]byte
133	offset int
134
135	key    [BlockSize]byte
136	keyLen int
137}
138
139func (d *digest) BlockSize() int { return BlockSize }
140
141func (d *digest) Size() int { return d.size }
142
143func (d *digest) Reset() {
144	d.h = iv
145	d.h[0] ^= uint64(d.size) | (uint64(d.keyLen) << 8) | (1 << 16) | (1 << 24)
146	d.offset, d.c[0], d.c[1] = 0, 0, 0
147	if d.keyLen > 0 {
148		d.block = d.key
149		d.offset = BlockSize
150	}
151}
152
153func (d *digest) Write(p []byte) (n int, err error) {
154	n = len(p)
155
156	if d.offset > 0 {
157		remaining := BlockSize - d.offset
158		if n <= remaining {
159			d.offset += copy(d.block[d.offset:], p)
160			return
161		}
162		copy(d.block[d.offset:], p[:remaining])
163		hashBlocks(&d.h, &d.c, 0, d.block[:])
164		d.offset = 0
165		p = p[remaining:]
166	}
167
168	if length := len(p); length > BlockSize {
169		nn := length &^ (BlockSize - 1)
170		if length == nn {
171			nn -= BlockSize
172		}
173		hashBlocks(&d.h, &d.c, 0, p[:nn])
174		p = p[nn:]
175	}
176
177	if len(p) > 0 {
178		d.offset += copy(d.block[:], p)
179	}
180
181	return
182}
183
184func (d *digest) Sum(sum []byte) []byte {
185	var hash [Size]byte
186	d.finalize(&hash)
187	return append(sum, hash[:d.size]...)
188}
189
190func (d *digest) finalize(hash *[Size]byte) {
191	var block [BlockSize]byte
192	copy(block[:], d.block[:d.offset])
193	remaining := uint64(BlockSize - d.offset)
194
195	c := d.c
196	if c[0] < remaining {
197		c[1]--
198	}
199	c[0] -= remaining
200
201	h := d.h
202	hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
203
204	for i, v := range h {
205		binary.LittleEndian.PutUint64(hash[8*i:], v)
206	}
207}
208