mirror of
https://github.com/emmansun/gmsm.git
synced 2025-10-14 07:10:45 +08:00
131 lines
4.1 KiB
Go
131 lines
4.1 KiB
Go
![]() |
// Copyright 2025 Sun Yimin. All rights reserved.
|
||
|
// Use of this source code is governed by a MIT-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package cipher
|
||
|
|
||
|
import "github.com/emmansun/gmsm/internal/byteorder"
|
||
|
|
||
|
const (
|
||
|
ghashBlockSize = 16
|
||
|
)
|
||
|
|
||
|
// ghashFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM
|
||
|
// standard and make binary.BigEndian suitable for marshaling these values, the
|
||
|
// bits are stored in big endian order. For example:
|
||
|
//
|
||
|
// the coefficient of x⁰ can be obtained by v.low >> 63.
|
||
|
// the coefficient of x⁶³ can be obtained by v.low & 1.
|
||
|
// the coefficient of x⁶⁴ can be obtained by v.high >> 63.
|
||
|
// the coefficient of x¹²⁷ can be obtained by v.high & 1.
|
||
|
type ghashFieldElement struct {
|
||
|
low, high uint64
|
||
|
}
|
||
|
|
||
|
// reverseBits reverses the order of the bits of 4-bit number in i.
|
||
|
func reverseBits(i int) int {
|
||
|
i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
|
||
|
i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
|
||
|
return i
|
||
|
}
|
||
|
|
||
|
// hctrAdd adds two elements of GF(2¹²⁸) and returns the sum.
|
||
|
func ghashAdd(x, y *ghashFieldElement) ghashFieldElement {
|
||
|
// Addition in a characteristic 2 field is just XOR.
|
||
|
return ghashFieldElement{x.low ^ y.low, x.high ^ y.high}
|
||
|
}
|
||
|
|
||
|
// hctrDouble returns the result of doubling an element of GF(2¹²⁸).
|
||
|
func ghashDouble(x *ghashFieldElement) (double ghashFieldElement) {
|
||
|
msbSet := x.high&1 == 1
|
||
|
|
||
|
// Because of the bit-ordering, doubling is actually a right shift.
|
||
|
double.high = x.high >> 1
|
||
|
double.high |= x.low << 63
|
||
|
double.low = x.low >> 1
|
||
|
|
||
|
// If the most-significant bit was set before shifting then it,
|
||
|
// conceptually, becomes a term of x^128. This is greater than the
|
||
|
// irreducible polynomial so the result has to be reduced. The
|
||
|
// irreducible polynomial is 1+x+x^2+x^7+x^128. We can subtract that to
|
||
|
// eliminate the term at x^128 which also means subtracting the other
|
||
|
// four terms. In characteristic 2 fields, subtraction == addition ==
|
||
|
// XOR.
|
||
|
if msbSet {
|
||
|
double.low ^= 0xe100000000000000
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// ghashReductionTable is stored irreducible polynomial's double & add precomputed results.
|
||
|
// 0000 - 0
|
||
|
// 0001 - irreducible polynomial >> 3
|
||
|
// 0010 - irreducible polynomial >> 2
|
||
|
// 0011 - (irreducible polynomial >> 3 xor irreducible polynomial >> 2)
|
||
|
// ...
|
||
|
// 1000 - just the irreducible polynomial
|
||
|
var ghashReductionTable = []uint16{
|
||
|
0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
|
||
|
0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
|
||
|
}
|
||
|
|
||
|
// ghashMul sets y to y*H, where H is the GHASH key, fixed during New.
|
||
|
func ghashMul(productTable *[16]ghashFieldElement, y *ghashFieldElement) {
|
||
|
var z ghashFieldElement
|
||
|
|
||
|
// Eliminate bounds checks in the loop.
|
||
|
_ = ghashReductionTable[0xf]
|
||
|
|
||
|
for i := 0; i < 2; i++ {
|
||
|
word := y.high
|
||
|
if i == 1 {
|
||
|
word = y.low
|
||
|
}
|
||
|
|
||
|
// Multiplication works by multiplying z by 16 and adding in
|
||
|
// one of the precomputed multiples of hash key.
|
||
|
for j := 0; j < 64; j += 4 {
|
||
|
msw := z.high & 0xf
|
||
|
z.high >>= 4
|
||
|
z.high |= z.low << 60
|
||
|
z.low >>= 4
|
||
|
z.low ^= uint64(ghashReductionTable[msw]) << 48
|
||
|
|
||
|
// the values in |table| are ordered for
|
||
|
// little-endian bit positions.
|
||
|
t := &productTable[word&0xf]
|
||
|
|
||
|
z.low ^= t.low
|
||
|
z.high ^= t.high
|
||
|
word >>= 4
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*y = z
|
||
|
}
|
||
|
|
||
|
// updateBlocks extends y with more polynomial terms from blocks, based on
|
||
|
// Horner's rule. There must be a multiple of gcmBlockSize bytes in blocks.
|
||
|
func updateBlocks(productTable *[16]ghashFieldElement, y *ghashFieldElement, blocks []byte) {
|
||
|
for len(blocks) > 0 {
|
||
|
y.low ^= byteorder.BEUint64(blocks)
|
||
|
y.high ^= byteorder.BEUint64(blocks[8:])
|
||
|
ghashMul(productTable, y)
|
||
|
blocks = blocks[blockSize:]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ghashUpdate extends y with more polynomial terms from data. If data is not a
|
||
|
// multiple of gcmBlockSize bytes long then the remainder is zero padded.
|
||
|
func ghashUpdate(productTable *[16]ghashFieldElement, y *ghashFieldElement, data []byte) {
|
||
|
fullBlocks := (len(data) >> 4) << 4
|
||
|
updateBlocks(productTable, y, data[:fullBlocks])
|
||
|
|
||
|
if len(data) != fullBlocks {
|
||
|
var partialBlock [blockSize]byte
|
||
|
copy(partialBlock[:], data[fullBlocks:])
|
||
|
updateBlocks(productTable, y, partialBlock[:])
|
||
|
}
|
||
|
}
|