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 }