1// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
2// Copyright (c) 2017 Yawning Angel <yawning at schwanenlied dot me>
3//
4// Permission is hereby granted, free of charge, to any person obtaining
5// a copy of this software and associated documentation files (the
6// "Software"), to deal in the Software without restriction, including
7// without limitation the rights to use, copy, modify, merge, publish,
8// distribute, sublicense, and/or sell copies of the Software, and to
9// permit persons to whom the Software is furnished to do so, subject to
10// the following conditions:
11//
12// The above copyright notice and this permission notice shall be
13// included in all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23
24package ghash
25
26import (
27	"bytes"
28	"crypto/rand"
29	"encoding/binary"
30	"encoding/hex"
31	"testing"
32)
33
34// The test vectors are shamelessly stolen from "The  Galois/Counter Mode of
35// Operation (GCM)", which is what BearSSL does.
36//
37// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
38
39var ghashVectors = []struct {
40	h string
41	a string
42	c string
43	y string
44}{
45	{
46		"66e94bd4ef8a2c3b884cfa59ca342b2e",
47		"",
48		"",
49		"00000000000000000000000000000000",
50	},
51	{
52		"66e94bd4ef8a2c3b884cfa59ca342b2e",
53		"",
54		"0388dace60b6a392f328c2b971b2fe78",
55		"f38cbb1ad69223dcc3457ae5b6b0f885",
56	},
57	{
58		"b83b533708bf535d0aa6e52980d53b78",
59		"",
60		"42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
61		"7f1b32b81b820d02614f8895ac1d4eac",
62	},
63	{
64		"b83b533708bf535d0aa6e52980d53b78",
65		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
66		"42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
67		"698e57f70e6ecc7fd9463b7260a9ae5f",
68	},
69	{
70		"b83b533708bf535d0aa6e52980d53b78",
71		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
72		"61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
73		"df586bb4c249b92cb6922877e444d37b",
74	},
75	{
76		"b83b533708bf535d0aa6e52980d53b78",
77		"feedfacedeadbeeffeedfacedeadbeefabaddad2",
78		"8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5",
79		"1c5afe9760d3932f3c9a878aac3dc3de",
80	},
81}
82
83func gcmGHASH(y, h *[blockSize]byte, a, c []byte) {
84	var p [blockSize]byte
85	Ghash(y, h, a)
86	Ghash(y, h, c)
87	binary.BigEndian.PutUint32(p[4:], uint32(len(a))<<3)
88	binary.BigEndian.PutUint32(p[12:], uint32(len(c))<<3)
89	Ghash(y, h, p[:])
90}
91
92func TestGHASH(t *testing.T) {
93	for i, vec := range ghashVectors {
94		hh, err := hex.DecodeString(vec.h[:])
95		if err != nil {
96			t.Fatal(err)
97		}
98		a, err := hex.DecodeString(vec.a[:])
99		if err != nil {
100			t.Fatal(err)
101		}
102		c, err := hex.DecodeString(vec.c[:])
103		if err != nil {
104			t.Fatal(err)
105		}
106		yy, err := hex.DecodeString(vec.y[:])
107		if err != nil {
108			t.Fatal(err)
109		}
110
111		var h, y [blockSize]byte
112		copy(h[:], hh)
113
114		gcmGHASH(&y, &h, a, c)
115		assertEqual(t, i, yy[:], y[:])
116	}
117}
118
119func assertEqual(t *testing.T, idx int, expected, actual []byte) {
120	if !bytes.Equal(expected, actual) {
121		for i, v := range actual {
122			if expected[i] != v {
123				t.Errorf("[%d] first mismatch at offset: %d (%02x != %02x)", idx, i, expected[i], v)
124				break
125			}
126		}
127		t.Errorf("expected: %s", hex.Dump(expected))
128		t.Errorf("actual: %s", hex.Dump(actual))
129		t.FailNow()
130	}
131}
132
133var ghashBenchOutput [blockSize]byte
134
135func BenchmarkGHASH(b *testing.B) {
136	var y, h [blockSize]byte
137	var buf [8192]byte
138
139	if _, err := rand.Read(buf[:]); err != nil {
140		b.Error(err)
141		b.Fail()
142	}
143	if _, err := rand.Read(h[:]); err != nil {
144		b.Error(err)
145		b.Fail()
146	}
147
148	b.SetBytes(int64(len(buf)))
149	b.ResetTimer()
150	for i := 0; i < b.N; i++ {
151		Ghash(&y, &h, buf[:])
152	}
153	b.StopTimer()
154	copy(ghashBenchOutput[:], y[:])
155}
156