Skip to content

Commit 189e009

Browse files
ValarDragonFiloSottile
authored andcommitted
blake2b,blake2s: implement BinaryMarshaler, BinaryUnmarshaler
The marshal method allows the hash's internal state to be serialized and unmarshaled at a later time, without having the re-write the entire stream of data that was already written to the hash. Fixes golang/go#24548 Change-Id: I82358c34181fc815f85d5d1509fb2fe0e62e40bd Reviewed-on: https://go-review.googlesource.com/103241 Reviewed-by: Filippo Valsorda <[email protected]> Run-TryBot: Filippo Valsorda <[email protected]>
1 parent 33e3a4e commit 189e009

File tree

4 files changed

+222
-0
lines changed

4 files changed

+222
-0
lines changed

blake2b/blake2b.go

+68
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
9292
// values equal or greater than:
9393
// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
9494
// - 16 if BLAKE2b is used as a MAC function (The key is at least 16 bytes long).
95+
// When the key is nil, the returned hash.Hash implements BinaryMarshaler
96+
// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash.
9597
func New(size int, key []byte) (hash.Hash, error) { return newDigest(size, key) }
9698

9799
func newDigest(hashSize int, key []byte) (*digest, error) {
@@ -150,6 +152,50 @@ type digest struct {
150152
keyLen int
151153
}
152154

155+
const (
156+
magic = "b2b"
157+
marshaledSize = len(magic) + 8*8 + 2*8 + 1 + BlockSize + 1
158+
)
159+
160+
func (d *digest) MarshalBinary() ([]byte, error) {
161+
if d.keyLen != 0 {
162+
return nil, errors.New("crypto/blake2b: cannot marshal MACs")
163+
}
164+
b := make([]byte, 0, marshaledSize)
165+
b = append(b, magic...)
166+
for i := 0; i < 8; i++ {
167+
b = appendUint64(b, d.h[i])
168+
}
169+
b = appendUint64(b, d.c[0])
170+
b = appendUint64(b, d.c[1])
171+
// Maximum value for size is 64
172+
b = append(b, byte(d.size))
173+
b = append(b, d.block[:]...)
174+
b = append(b, byte(d.offset))
175+
return b, nil
176+
}
177+
178+
func (d *digest) UnmarshalBinary(b []byte) error {
179+
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
180+
return errors.New("crypto/blake2b: invalid hash state identifier")
181+
}
182+
if len(b) != marshaledSize {
183+
return errors.New("crypto/blake2b: invalid hash state size")
184+
}
185+
b = b[len(magic):]
186+
for i := 0; i < 8; i++ {
187+
b, d.h[i] = consumeUint64(b)
188+
}
189+
b, d.c[0] = consumeUint64(b)
190+
b, d.c[1] = consumeUint64(b)
191+
d.size = int(b[0])
192+
b = b[1:]
193+
copy(d.block[:], b[:BlockSize])
194+
b = b[BlockSize:]
195+
d.offset = int(b[0])
196+
return nil
197+
}
198+
153199
func (d *digest) BlockSize() int { return BlockSize }
154200

155201
func (d *digest) Size() int { return d.size }
@@ -219,3 +265,25 @@ func (d *digest) finalize(hash *[Size]byte) {
219265
binary.LittleEndian.PutUint64(hash[8*i:], v)
220266
}
221267
}
268+
269+
func appendUint64(b []byte, x uint64) []byte {
270+
var a [8]byte
271+
binary.BigEndian.PutUint64(a[:], x)
272+
return append(b, a[:]...)
273+
}
274+
275+
func appendUint32(b []byte, x uint32) []byte {
276+
var a [4]byte
277+
binary.BigEndian.PutUint32(a[:], x)
278+
return append(b, a[:]...)
279+
}
280+
281+
func consumeUint64(b []byte) ([]byte, uint64) {
282+
x := binary.BigEndian.Uint64(b)
283+
return b[8:], x
284+
}
285+
286+
func consumeUint32(b []byte) ([]byte, uint32) {
287+
x := binary.BigEndian.Uint32(b)
288+
return b[4:], x
289+
}

blake2b/blake2b_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package blake2b
66

77
import (
88
"bytes"
9+
"encoding"
910
"encoding/hex"
1011
"fmt"
1112
"hash"
@@ -69,6 +70,54 @@ func TestHashes2X(t *testing.T) {
6970
testHashes2X(t)
7071
}
7172

73+
func TestMarshal(t *testing.T) {
74+
input := make([]byte, 255)
75+
for i := range input {
76+
input[i] = byte(i)
77+
}
78+
for _, size := range []int{Size, Size256, Size384, 12, 25, 63} {
79+
for i := 0; i < 256; i++ {
80+
h, err := New(size, nil)
81+
if err != nil {
82+
t.Fatalf("size=%d, len(input)=%d: error from New(%v, nil): %v", size, i, size, err)
83+
}
84+
h2, err := New(size, nil)
85+
if err != nil {
86+
t.Fatalf("size=%d, len(input)=%d: error from New(%v, nil): %v", size, i, size, err)
87+
}
88+
89+
h.Write(input[:i/2])
90+
halfstate, err := h.(encoding.BinaryMarshaler).MarshalBinary()
91+
if err != nil {
92+
t.Fatalf("size=%d, len(input)=%d: could not marshal: %v", size, i, err)
93+
}
94+
err = h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(halfstate)
95+
if err != nil {
96+
t.Fatalf("size=%d, len(input)=%d: could not unmarshal: %v", size, i, err)
97+
}
98+
99+
h.Write(input[i/2 : i])
100+
sum := h.Sum(nil)
101+
h2.Write(input[i/2 : i])
102+
sum2 := h2.Sum(nil)
103+
104+
if !bytes.Equal(sum, sum2) {
105+
t.Fatalf("size=%d, len(input)=%d: results do not match; sum = %v, sum2 = %v", size, i, sum, sum2)
106+
}
107+
108+
h3, err := New(size, nil)
109+
if err != nil {
110+
t.Fatalf("size=%d, len(input)=%d: error from New(%v, nil): %v", size, i, size, err)
111+
}
112+
h3.Write(input[:i])
113+
sum3 := h3.Sum(nil)
114+
if !bytes.Equal(sum, sum3) {
115+
t.Fatalf("size=%d, len(input)=%d: sum = %v, want %v", size, i, sum, sum3)
116+
}
117+
}
118+
}
119+
}
120+
72121
func testHashes(t *testing.T) {
73122
key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")
74123

blake2s/blake2s.go

+57
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ func Sum256(data []byte) [Size]byte {
4949

5050
// New256 returns a new hash.Hash computing the BLAKE2s-256 checksum. A non-nil
5151
// key turns the hash into a MAC. The key must between zero and 32 bytes long.
52+
// When the key is nil, the returned hash.Hash implements BinaryMarshaler
53+
// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash.
5254
func New256(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
5355

5456
// New128 returns a new hash.Hash computing the BLAKE2s-128 checksum given a
@@ -120,6 +122,50 @@ type digest struct {
120122
keyLen int
121123
}
122124

125+
const (
126+
magic = "b2s"
127+
marshaledSize = len(magic) + 8*4 + 2*4 + 1 + BlockSize + 1
128+
)
129+
130+
func (d *digest) MarshalBinary() ([]byte, error) {
131+
if d.keyLen != 0 {
132+
return nil, errors.New("crypto/blake2s: cannot marshal MACs")
133+
}
134+
b := make([]byte, 0, marshaledSize)
135+
b = append(b, magic...)
136+
for i := 0; i < 8; i++ {
137+
b = appendUint32(b, d.h[i])
138+
}
139+
b = appendUint32(b, d.c[0])
140+
b = appendUint32(b, d.c[1])
141+
// Maximum value for size is 32
142+
b = append(b, byte(d.size))
143+
b = append(b, d.block[:]...)
144+
b = append(b, byte(d.offset))
145+
return b, nil
146+
}
147+
148+
func (d *digest) UnmarshalBinary(b []byte) error {
149+
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
150+
return errors.New("crypto/blake2s: invalid hash state identifier")
151+
}
152+
if len(b) != marshaledSize {
153+
return errors.New("crypto/blake2s: invalid hash state size")
154+
}
155+
b = b[len(magic):]
156+
for i := 0; i < 8; i++ {
157+
b, d.h[i] = consumeUint32(b)
158+
}
159+
b, d.c[0] = consumeUint32(b)
160+
b, d.c[1] = consumeUint32(b)
161+
d.size = int(b[0])
162+
b = b[1:]
163+
copy(d.block[:], b[:BlockSize])
164+
b = b[BlockSize:]
165+
d.offset = int(b[0])
166+
return nil
167+
}
168+
123169
func (d *digest) BlockSize() int { return BlockSize }
124170

125171
func (d *digest) Size() int { return d.size }
@@ -185,3 +231,14 @@ func (d *digest) finalize(hash *[Size]byte) {
185231
binary.LittleEndian.PutUint32(hash[4*i:], v)
186232
}
187233
}
234+
235+
func appendUint32(b []byte, x uint32) []byte {
236+
var a [4]byte
237+
binary.BigEndian.PutUint32(a[:], x)
238+
return append(b, a[:]...)
239+
}
240+
241+
func consumeUint32(b []byte) ([]byte, uint32) {
242+
x := binary.BigEndian.Uint32(b)
243+
return b[4:], x
244+
}

blake2s/blake2s_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package blake2s
66

77
import (
8+
"bytes"
9+
"encoding"
810
"encoding/hex"
911
"fmt"
1012
"testing"
@@ -64,6 +66,52 @@ func TestHashes2X(t *testing.T) {
6466
testHashes2X(t)
6567
}
6668

69+
func TestMarshal(t *testing.T) {
70+
input := make([]byte, 255)
71+
for i := range input {
72+
input[i] = byte(i)
73+
}
74+
for i := 0; i < 256; i++ {
75+
h, err := New256(nil)
76+
if err != nil {
77+
t.Fatalf("len(input)=%d: error from New256(nil): %v", i, err)
78+
}
79+
h2, err := New256(nil)
80+
if err != nil {
81+
t.Fatalf("len(input)=%d: error from New256(nil): %v", i, err)
82+
}
83+
84+
h.Write(input[:i/2])
85+
halfstate, err := h.(encoding.BinaryMarshaler).MarshalBinary()
86+
if err != nil {
87+
t.Fatalf("len(input)=%d: could not marshal: %v", i, err)
88+
}
89+
err = h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(halfstate)
90+
if err != nil {
91+
t.Fatalf("len(input)=%d: could not unmarshal: %v", i, err)
92+
}
93+
94+
h.Write(input[i/2 : i])
95+
sum := h.Sum(nil)
96+
h2.Write(input[i/2 : i])
97+
sum2 := h2.Sum(nil)
98+
99+
if !bytes.Equal(sum, sum2) {
100+
t.Fatalf("len(input)=%d: results do not match; sum = %v, sum2 = %v", i, sum, sum2)
101+
}
102+
103+
h3, err := New256(nil)
104+
if err != nil {
105+
t.Fatalf("len(input)=%d: error from New256(nil): %v", i, err)
106+
}
107+
h3.Write(input[:i])
108+
sum3 := h3.Sum(nil)
109+
if !bytes.Equal(sum, sum3) {
110+
t.Fatalf("len(input)=%d: sum = %v, want %v", i, sum, sum3)
111+
}
112+
}
113+
}
114+
67115
func testHashes(t *testing.T) {
68116
key, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
69117

0 commit comments

Comments
 (0)