github.com/qv2ray/smaead@v0.0.0-20211021072225-a01f7e01d185/partial_gcm.go (about) 1 package smaead 2 3 import ( 4 "crypto/cipher" 5 "encoding/binary" 6 "errors" 7 ) 8 9 // Gcm represents a Galois Counter Mode with a specific key. See 10 // https://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf 11 type Gcm struct { 12 cipher cipher.Block 13 // productTable contains the first sixteen powers of the key, H. 14 // However, they are in bit reversed order. See NewGCMWithNonceSize. 15 productTable [16]gcmFieldElement 16 } 17 18 func NewPartialGCM(cipher cipher.Block) (*Gcm, error) { 19 if cipher.BlockSize() != gcmBlockSize { 20 return nil, errors.New("cipher: NewGCM requires 128-bit block cipher") 21 } 22 var key [gcmBlockSize]byte 23 cipher.Encrypt(key[:], key[:]) 24 g := &Gcm{cipher: cipher} 25 // We precompute 16 multiples of |key|. However, when we do lookups 26 // into this table we'll be using bits from a field element and 27 // therefore the bits will be in the reverse order. So normally one 28 // would expect, say, 4*key to be in index 4 of the table but due to 29 // this bit ordering it will actually be in index 0010 (base 2) = 2. 30 x := gcmFieldElement{ 31 binary.BigEndian.Uint64(key[:8]), 32 binary.BigEndian.Uint64(key[8:]), 33 } 34 g.productTable[reverseBits(1)] = x 35 for i := 2; i < 16; i += 2 { 36 g.productTable[reverseBits(i)] = gcmDouble(&g.productTable[reverseBits(i/2)]) 37 g.productTable[reverseBits(i+1)] = gcmAdd(&g.productTable[reverseBits(i)], &x) 38 } 39 return g, nil 40 } 41 42 const ( 43 gcmBlockSize = 16 44 gcmStandardNonceSize = 12 45 ) 46 47 func (g *Gcm) OpenWithoutCheck(dst, nonce, ciphertext []byte) []byte { 48 if len(nonce) != gcmStandardNonceSize { 49 panic("smaead.gcm: incorrect nonce length given to GCM") 50 } 51 var counter [gcmBlockSize]byte 52 // set ctr 53 g.deriveCounter(&counter, nonce) 54 gcmInc32(&counter) 55 ret, out := sliceForAppend(dst, len(ciphertext)) 56 if inexactOverlap(out, ciphertext) { 57 panic("smaead.gcm: invalid buffer overlap") 58 } 59 g.counterCrypt(out, ciphertext, &counter) 60 return ret 61 } 62 63 // counterCrypt crypts in to out using g.cipher in counter mode. 64 func (g *Gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) { 65 var mask [gcmBlockSize]byte 66 for len(in) >= gcmBlockSize { 67 g.cipher.Encrypt(mask[:], counter[:]) 68 gcmInc32(counter) 69 xorWords(out, in, mask[:]) 70 out = out[gcmBlockSize:] 71 in = in[gcmBlockSize:] 72 } 73 if len(in) > 0 { 74 g.cipher.Encrypt(mask[:], counter[:]) 75 gcmInc32(counter) 76 xorBytes(out, in, mask[:]) 77 } 78 } 79 80 // deriveCounter computes the initial GCM counter state from the given nonce. 81 // See NIST SP 800-38D, section 7.1. This assumes that counter is filled with 82 // zeros on entry. 83 func (g *Gcm) deriveCounter(counter *[gcmBlockSize]byte, nonce []byte) { 84 // GCM has two modes of operation with respect to the initial counter 85 // state: a "fast path" for 96-bit (12-byte) nonces, and a "slow path" 86 // for nonces of other lengths. For a 96-bit nonce, the nonce, along 87 // with a four-byte big-endian counter starting at one, is used 88 // directly as the starting counter. For other nonce sizes, the counter 89 // is computed by passing it through the GHASH function. 90 if len(nonce) == gcmStandardNonceSize { 91 copy(counter[:], nonce) 92 counter[gcmBlockSize-1] = 1 93 } else { 94 panic("smaead.gcm: use 12 byte nonce!") 95 } 96 }