github.com/emmansun/gmsm@v0.29.1/cipher/hctr.go (about)

     1  package cipher
     2  
     3  import (
     4  	_cipher "crypto/cipher"
     5  	"encoding/binary"
     6  	"errors"
     7  
     8  	"github.com/emmansun/gmsm/internal/alias"
     9  	"github.com/emmansun/gmsm/internal/subtle"
    10  )
    11  
    12  // A LengthPreservingMode represents a block cipher running in a length preserving mode (HCTR,
    13  // HCTR2 etc).
    14  type LengthPreservingMode interface {
    15  	// EncryptBytes encrypts a number of plaintext bytes. The length of
    16  	// src must be NOT smaller than block size. Dst and src must overlap
    17  	// entirely or not at all.
    18  	//
    19  	// If len(dst) < len(src), EncryptBytes should panic. It is acceptable
    20  	// to pass a dst bigger than src, and in that case, Encrypt will
    21  	// only update dst[:len(src)] and will not touch the rest of dst.
    22  	//
    23  	// Multiple calls to EncryptBytes behave NOT same as if the concatenation of
    24  	// the src buffers was passed in a single run.
    25  	EncryptBytes(dst, src []byte)
    26  
    27  	// DecryptBytes decrypts a number of ciphertext bytes. The length of
    28  	// src must be NOT smaller than block size. Dst and src must overlap
    29  	// entirely or not at all.
    30  	//
    31  	// If len(dst) < len(src), DecryptBytes should panic. It is acceptable
    32  	// to pass a dst bigger than src, and in that case, DecryptBytes will
    33  	// only update dst[:len(src)] and will not touch the rest of dst.
    34  	//
    35  	// Multiple calls to DecryptBytes behave NOT same as if the concatenation of
    36  	// the src buffers was passed in a single run.
    37  	DecryptBytes(dst, src []byte)
    38  
    39  	// BlockSize returns the mode's block size.
    40  	BlockSize() int
    41  }
    42  
    43  // hctrFieldElement represents a value in GF(2¹²⁸). In order to reflect the HCTR
    44  // standard and make binary.BigEndian suitable for marshaling these values, the
    45  // bits are stored in big endian order. For example:
    46  //
    47  //	the coefficient of x⁰ can be obtained by v.low >> 63.
    48  //	the coefficient of x⁶³ can be obtained by v.low & 1.
    49  //	the coefficient of x⁶⁴ can be obtained by v.high >> 63.
    50  //	the coefficient of x¹²⁷ can be obtained by v.high & 1.
    51  type hctrFieldElement struct {
    52  	low, high uint64
    53  }
    54  
    55  // reverseBits reverses the order of the bits of 4-bit number in i.
    56  func reverseBits(i int) int {
    57  	i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
    58  	i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
    59  	return i
    60  }
    61  
    62  // hctrAdd adds two elements of GF(2¹²⁸) and returns the sum.
    63  func hctrAdd(x, y *hctrFieldElement) hctrFieldElement {
    64  	// Addition in a characteristic 2 field is just XOR.
    65  	return hctrFieldElement{x.low ^ y.low, x.high ^ y.high}
    66  }
    67  
    68  // hctrDouble returns the result of doubling an element of GF(2¹²⁸).
    69  func hctrDouble(x *hctrFieldElement) (double hctrFieldElement) {
    70  	msbSet := x.high&1 == 1
    71  
    72  	// Because of the bit-ordering, doubling is actually a right shift.
    73  	double.high = x.high >> 1
    74  	double.high |= x.low << 63
    75  	double.low = x.low >> 1
    76  
    77  	// If the most-significant bit was set before shifting then it,
    78  	// conceptually, becomes a term of x^128. This is greater than the
    79  	// irreducible polynomial so the result has to be reduced. The
    80  	// irreducible polynomial is 1+x+x^2+x^7+x^128. We can subtract that to
    81  	// eliminate the term at x^128 which also means subtracting the other
    82  	// four terms. In characteristic 2 fields, subtraction == addition ==
    83  	// XOR.
    84  	if msbSet {
    85  		double.low ^= 0xe100000000000000
    86  	}
    87  
    88  	return
    89  }
    90  
    91  // hctrReductionTable is stored irreducible polynomial's double & add precomputed results.
    92  // 0000 - 0
    93  // 0001 - irreducible polynomial >> 3
    94  // 0010 - irreducible polynomial >> 2
    95  // 0011 - (irreducible polynomial >> 3 xor irreducible polynomial >> 2)
    96  // ...
    97  // 1000 - just the irreducible polynomial
    98  var hctrReductionTable = []uint16{
    99  	0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
   100  	0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
   101  }
   102  
   103  // hctr represents a Varaible-Input-Length enciphering mode with a specific block cipher,
   104  // and specific tweak and a hash key. See
   105  // https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288
   106  // GB/T 17964-2021 第11章 带泛杂凑函数的计数器工作模式
   107  type hctr struct {
   108  	cipher _cipher.Block
   109  	tweak  [blockSize]byte
   110  	// productTable contains the first sixteen powers of the hash key.
   111  	// However, they are in bit reversed order.
   112  	productTable [16]hctrFieldElement
   113  }
   114  
   115  func (h *hctr) BlockSize() int {
   116  	return blockSize
   117  }
   118  
   119  // NewHCTR returns a [LengthPreservingMode] which encrypts/decrypts useing the given [Block]
   120  // in HCTR mode. The lenght of tweak and hash key must be the same as the [Block]'s block size.
   121  func NewHCTR(cipher _cipher.Block, tweak, hkey []byte) (LengthPreservingMode, error) {
   122  	if len(tweak) != blockSize || len(hkey) != blockSize {
   123  		return nil, errors.New("cipher: invalid tweak and/or hash key length")
   124  	}
   125  	c := &hctr{}
   126  	c.cipher = cipher
   127  	copy(c.tweak[:], tweak)
   128  	// We precompute 16 multiples of |key|. However, when we do lookups
   129  	// into this table we'll be using bits from a field element and
   130  	// therefore the bits will be in the reverse order. So normally one
   131  	// would expect, say, 4*key to be in index 4 of the table but due to
   132  	// this bit ordering it will actually be in index 0010 (base 2) = 2.
   133  	x := hctrFieldElement{
   134  		binary.BigEndian.Uint64(hkey[:8]),
   135  		binary.BigEndian.Uint64(hkey[8:blockSize]),
   136  	}
   137  	c.productTable[reverseBits(1)] = x
   138  
   139  	for i := 2; i < 16; i += 2 {
   140  		c.productTable[reverseBits(i)] = hctrDouble(&c.productTable[reverseBits(i/2)])
   141  		c.productTable[reverseBits(i+1)] = hctrAdd(&c.productTable[reverseBits(i)], &x)
   142  	}
   143  	return c, nil
   144  }
   145  
   146  // mul sets y to y*H, where H is the GCM key, fixed during NewHCTR.
   147  func (h *hctr) mul(y *hctrFieldElement) {
   148  	var z hctrFieldElement
   149  
   150  	// Eliminate bounds checks in the loop.
   151  	_ = hctrReductionTable[0xf]
   152  
   153  	for i := 0; i < 2; i++ {
   154  		word := y.high
   155  		if i == 1 {
   156  			word = y.low
   157  		}
   158  
   159  		// Multiplication works by multiplying z by 16 and adding in
   160  		// one of the precomputed multiples of hash key.
   161  		for j := 0; j < 64; j += 4 {
   162  			msw := z.high & 0xf
   163  			z.high >>= 4
   164  			z.high |= z.low << 60
   165  			z.low >>= 4
   166  			z.low ^= uint64(hctrReductionTable[msw]) << 48
   167  
   168  			// the values in |table| are ordered for
   169  			// little-endian bit positions. See the comment
   170  			// in NewHCTR.
   171  			t := &h.productTable[word&0xf]
   172  
   173  			z.low ^= t.low
   174  			z.high ^= t.high
   175  			word >>= 4
   176  		}
   177  	}
   178  
   179  	*y = z
   180  }
   181  
   182  func (h *hctr) updateBlock(block []byte, y *hctrFieldElement) {
   183  	y.low ^= binary.BigEndian.Uint64(block)
   184  	y.high ^= binary.BigEndian.Uint64(block[8:])
   185  	h.mul(y)
   186  }
   187  
   188  // Universal Hash Function.
   189  // Chapter 3.3 in https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288.
   190  func (h *hctr) uhash(m []byte, out *[blockSize]byte) {
   191  	var y hctrFieldElement
   192  	msg := m
   193  	// update blocks
   194  	for len(msg) >= blockSize {
   195  		h.updateBlock(msg, &y)
   196  		msg = msg[blockSize:]
   197  	}
   198  	// update partial block & tweak
   199  	if len(msg) > 0 {
   200  		var partialBlock [blockSize]byte
   201  		copy(partialBlock[:], msg)
   202  		copy(partialBlock[len(msg):], h.tweak[:])
   203  		h.updateBlock(partialBlock[:], &y)
   204  
   205  		copy(partialBlock[:], h.tweak[len(msg):])
   206  		for i := len(msg); i < blockSize; i++ {
   207  			partialBlock[i] = 0
   208  		}
   209  		h.updateBlock(partialBlock[:], &y)
   210  	} else {
   211  		h.updateBlock(h.tweak[:], &y)
   212  	}
   213  	// update bit string length (|M|)₂
   214  	y.high ^= uint64(len(m)+blockSize) * 8
   215  	h.mul(&y)
   216  	// output result
   217  	binary.BigEndian.PutUint64(out[:], y.low)
   218  	binary.BigEndian.PutUint64(out[8:], y.high)
   219  }
   220  
   221  func (h *hctr) EncryptBytes(ciphertext, plaintext []byte) {
   222  	if len(ciphertext) < len(plaintext) {
   223  		panic("cipher: ciphertext is smaller than plaintext")
   224  	}
   225  	if len(plaintext) < blockSize {
   226  		panic("cipher: plaintext length is smaller than the block size")
   227  	}
   228  	if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
   229  		panic("cipher: invalid buffer overlap")
   230  	}
   231  
   232  	var z1, z2 [blockSize]byte
   233  	// a) z1 generation
   234  	h.uhash(plaintext[blockSize:], &z1)
   235  	subtle.XORBytes(z1[:], z1[:], plaintext[:blockSize])
   236  	// b) z2 generation
   237  	h.cipher.Encrypt(z2[:], z1[:])
   238  	// c) CTR
   239  	subtle.XORBytes(z1[:], z1[:], z2[:])
   240  	h.ctr(ciphertext[blockSize:], plaintext[blockSize:], &z1)
   241  	// d) first ciphertext block generation
   242  	h.uhash(ciphertext[blockSize:], &z1)
   243  	subtle.XORBytes(ciphertext, z2[:], z1[:])
   244  }
   245  
   246  func (h *hctr) DecryptBytes(plaintext, ciphertext []byte) {
   247  	if len(plaintext) < len(ciphertext) {
   248  		panic("cipher: plaintext is smaller than cihpertext")
   249  	}
   250  	if len(ciphertext) < blockSize {
   251  		panic("cipher: ciphertext length is smaller than the block size")
   252  	}
   253  	if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
   254  		panic("cipher: invalid buffer overlap")
   255  	}
   256  
   257  	var z1, z2 [blockSize]byte
   258  
   259  	// a) z2 generation
   260  	h.uhash(ciphertext[blockSize:], &z2)
   261  	subtle.XORBytes(z2[:], z2[:], ciphertext[:blockSize])
   262  	// b) z1 generation
   263  	h.cipher.Decrypt(z1[:], z2[:])
   264  	// c) CTR
   265  	subtle.XORBytes(z2[:], z2[:], z1[:])
   266  	h.ctr(plaintext[blockSize:], ciphertext[blockSize:], &z2)
   267  	// d) first plaintext block generation
   268  	h.uhash(plaintext[blockSize:], &z2)
   269  	subtle.XORBytes(plaintext, z2[:], z1[:])
   270  }
   271  
   272  func (h *hctr) ctr(dst, src []byte, baseCtr *[blockSize]byte) {
   273  	ctr := make([]byte, blockSize)
   274  	num := make([]byte, blockSize)
   275  	i := uint64(1)
   276  
   277  	if concCipher, ok := h.cipher.(concurrentBlocks); ok {
   278  		batchSize := concCipher.Concurrency() * blockSize
   279  		if len(src) >= batchSize {
   280  			var ctrs []byte = make([]byte, batchSize)
   281  			for len(src) >= batchSize {
   282  				for j := 0; j < concCipher.Concurrency(); j++ {
   283  					// (i)₂
   284  					binary.BigEndian.PutUint64(num[blockSize-8:], i)
   285  					subtle.XORBytes(ctrs[j*blockSize:], baseCtr[:], num)
   286  					i++
   287  				}
   288  				concCipher.EncryptBlocks(ctrs, ctrs)
   289  				subtle.XORBytes(dst, src, ctrs)
   290  				src = src[batchSize:]
   291  				dst = dst[batchSize:]
   292  			}
   293  		}
   294  	}
   295  
   296  	for len(src) > 0 {
   297  		// (i)₂
   298  		binary.BigEndian.PutUint64(num[blockSize-8:], i)
   299  		subtle.XORBytes(ctr, baseCtr[:], num)
   300  		h.cipher.Encrypt(ctr, ctr)
   301  		n := subtle.XORBytes(dst, src, ctr)
   302  		src = src[n:]
   303  		dst = dst[n:]
   304  		i++
   305  	}
   306  }