github.com/studentmain/smaead@v0.0.0-20210101171653-e876413b9e86/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  }