github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/crypto/aes/gcm_ppc64x.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build ppc64le || ppc64
     6  
     7  package aes
     8  
     9  import (
    10  	"crypto/cipher"
    11  	"crypto/subtle"
    12  	"encoding/binary"
    13  	"errors"
    14  	"runtime"
    15  )
    16  
    17  // This file implements GCM using an optimized GHASH function.
    18  
    19  //go:noescape
    20  func gcmInit(productTable *[256]byte, h []byte)
    21  
    22  //go:noescape
    23  func gcmHash(output []byte, productTable *[256]byte, inp []byte, len int)
    24  
    25  //go:noescape
    26  func gcmMul(output []byte, productTable *[256]byte)
    27  
    28  const (
    29  	gcmCounterSize       = 16
    30  	gcmBlockSize         = 16
    31  	gcmTagSize           = 16
    32  	gcmStandardNonceSize = 12
    33  )
    34  
    35  var errOpen = errors.New("cipher: message authentication failed")
    36  
    37  // Assert that aesCipherGCM implements the gcmAble interface.
    38  var _ gcmAble = (*aesCipherAsm)(nil)
    39  
    40  type gcmAsm struct {
    41  	cipher *aesCipherAsm
    42  	// ks is the key schedule, the length of which depends on the size of
    43  	// the AES key.
    44  	ks []uint32
    45  	// productTable contains pre-computed multiples of the binary-field
    46  	// element used in GHASH.
    47  	productTable [256]byte
    48  	// nonceSize contains the expected size of the nonce, in bytes.
    49  	nonceSize int
    50  	// tagSize contains the size of the tag, in bytes.
    51  	tagSize int
    52  }
    53  
    54  // NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
    55  // called by crypto/cipher.NewGCM via the gcmAble interface.
    56  func (c *aesCipherAsm) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) {
    57  	var h1, h2 uint64
    58  	g := &gcmAsm{cipher: c, ks: c.enc, nonceSize: nonceSize, tagSize: tagSize}
    59  
    60  	hle := make([]byte, gcmBlockSize)
    61  
    62  	c.Encrypt(hle, hle)
    63  
    64  	// Reverse the bytes in each 8 byte chunk
    65  	// Load little endian, store big endian
    66  	if runtime.GOARCH == "ppc64le" {
    67  		h1 = binary.LittleEndian.Uint64(hle[:8])
    68  		h2 = binary.LittleEndian.Uint64(hle[8:])
    69  	} else {
    70  		h1 = binary.BigEndian.Uint64(hle[:8])
    71  		h2 = binary.BigEndian.Uint64(hle[8:])
    72  	}
    73  	binary.BigEndian.PutUint64(hle[:8], h1)
    74  	binary.BigEndian.PutUint64(hle[8:], h2)
    75  	gcmInit(&g.productTable, hle)
    76  
    77  	return g, nil
    78  }
    79  
    80  func (g *gcmAsm) NonceSize() int {
    81  	return g.nonceSize
    82  }
    83  
    84  func (g *gcmAsm) Overhead() int {
    85  	return g.tagSize
    86  }
    87  
    88  func sliceForAppend(in []byte, n int) (head, tail []byte) {
    89  	if total := len(in) + n; cap(in) >= total {
    90  		head = in[:total]
    91  	} else {
    92  		head = make([]byte, total)
    93  		copy(head, in)
    94  	}
    95  	tail = head[len(in):]
    96  	return
    97  }
    98  
    99  // deriveCounter computes the initial GCM counter state from the given nonce.
   100  func (g *gcmAsm) deriveCounter(counter *[gcmBlockSize]byte, nonce []byte) {
   101  	if len(nonce) == gcmStandardNonceSize {
   102  		copy(counter[:], nonce)
   103  		counter[gcmBlockSize-1] = 1
   104  	} else {
   105  		var hash [16]byte
   106  		g.paddedGHASH(&hash, nonce)
   107  		lens := gcmLengths(0, uint64(len(nonce))*8)
   108  		g.paddedGHASH(&hash, lens[:])
   109  		copy(counter[:], hash[:])
   110  	}
   111  }
   112  
   113  // counterCrypt encrypts in using AES in counter mode and places the result
   114  // into out. counter is the initial count value and will be updated with the next
   115  // count value. The length of out must be greater than or equal to the length
   116  // of in.
   117  func (g *gcmAsm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
   118  	var mask [gcmBlockSize]byte
   119  
   120  	for len(in) >= gcmBlockSize {
   121  		// Hint to avoid bounds check
   122  		_, _ = in[15], out[15]
   123  		g.cipher.Encrypt(mask[:], counter[:])
   124  		gcmInc32(counter)
   125  
   126  		// XOR 16 bytes each loop iteration in 8 byte chunks
   127  		in0 := binary.LittleEndian.Uint64(in[0:])
   128  		in1 := binary.LittleEndian.Uint64(in[8:])
   129  		m0 := binary.LittleEndian.Uint64(mask[:8])
   130  		m1 := binary.LittleEndian.Uint64(mask[8:])
   131  		binary.LittleEndian.PutUint64(out[:8], in0^m0)
   132  		binary.LittleEndian.PutUint64(out[8:], in1^m1)
   133  		out = out[16:]
   134  		in = in[16:]
   135  	}
   136  
   137  	if len(in) > 0 {
   138  		g.cipher.Encrypt(mask[:], counter[:])
   139  		gcmInc32(counter)
   140  		// XOR leftover bytes
   141  		for i, inb := range in {
   142  			out[i] = inb ^ mask[i]
   143  		}
   144  	}
   145  }
   146  
   147  // increments the rightmost 32-bits of the count value by 1.
   148  func gcmInc32(counterBlock *[16]byte) {
   149  	c := counterBlock[len(counterBlock)-4:]
   150  	x := binary.BigEndian.Uint32(c) + 1
   151  	binary.BigEndian.PutUint32(c, x)
   152  }
   153  
   154  // paddedGHASH pads data with zeroes until its length is a multiple of
   155  // 16-bytes. It then calculates a new value for hash using the ghash
   156  // algorithm.
   157  func (g *gcmAsm) paddedGHASH(hash *[16]byte, data []byte) {
   158  	if siz := len(data) - (len(data) % gcmBlockSize); siz > 0 {
   159  		gcmHash(hash[:], &g.productTable, data[:], siz)
   160  		data = data[siz:]
   161  	}
   162  	if len(data) > 0 {
   163  		var s [16]byte
   164  		copy(s[:], data)
   165  		gcmHash(hash[:], &g.productTable, s[:], len(s))
   166  	}
   167  }
   168  
   169  // auth calculates GHASH(ciphertext, additionalData), masks the result with
   170  // tagMask and writes the result to out.
   171  func (g *gcmAsm) auth(out, ciphertext, aad []byte, tagMask *[gcmTagSize]byte) {
   172  	var hash [16]byte
   173  	g.paddedGHASH(&hash, aad)
   174  	g.paddedGHASH(&hash, ciphertext)
   175  	lens := gcmLengths(uint64(len(aad))*8, uint64(len(ciphertext))*8)
   176  	g.paddedGHASH(&hash, lens[:])
   177  
   178  	copy(out, hash[:])
   179  	for i := range out {
   180  		out[i] ^= tagMask[i]
   181  	}
   182  }
   183  
   184  // Seal encrypts and authenticates plaintext. See the cipher.AEAD interface for
   185  // details.
   186  func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
   187  	if len(nonce) != g.nonceSize {
   188  		panic("cipher: incorrect nonce length given to GCM")
   189  	}
   190  	if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize {
   191  		panic("cipher: message too large for GCM")
   192  	}
   193  
   194  	ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize)
   195  
   196  	var counter, tagMask [gcmBlockSize]byte
   197  	g.deriveCounter(&counter, nonce)
   198  
   199  	g.cipher.Encrypt(tagMask[:], counter[:])
   200  	gcmInc32(&counter)
   201  
   202  	g.counterCrypt(out, plaintext, &counter)
   203  	g.auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask)
   204  
   205  	return ret
   206  }
   207  
   208  // Open authenticates and decrypts ciphertext. See the cipher.AEAD interface
   209  // for details.
   210  func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
   211  	if len(nonce) != g.nonceSize {
   212  		panic("cipher: incorrect nonce length given to GCM")
   213  	}
   214  	if len(ciphertext) < g.tagSize {
   215  		return nil, errOpen
   216  	}
   217  	if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(BlockSize)+uint64(g.tagSize) {
   218  		return nil, errOpen
   219  	}
   220  
   221  	tag := ciphertext[len(ciphertext)-g.tagSize:]
   222  	ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
   223  
   224  	var counter, tagMask [gcmBlockSize]byte
   225  	g.deriveCounter(&counter, nonce)
   226  
   227  	g.cipher.Encrypt(tagMask[:], counter[:])
   228  	gcmInc32(&counter)
   229  
   230  	var expectedTag [gcmTagSize]byte
   231  	g.auth(expectedTag[:], ciphertext, data, &tagMask)
   232  
   233  	ret, out := sliceForAppend(dst, len(ciphertext))
   234  
   235  	if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
   236  		for i := range out {
   237  			out[i] = 0
   238  		}
   239  		return nil, errOpen
   240  	}
   241  
   242  	g.counterCrypt(out, ciphertext, &counter)
   243  	return ret, nil
   244  }
   245  
   246  func gcmLengths(len0, len1 uint64) [16]byte {
   247  	return [16]byte{
   248  		byte(len0 >> 56),
   249  		byte(len0 >> 48),
   250  		byte(len0 >> 40),
   251  		byte(len0 >> 32),
   252  		byte(len0 >> 24),
   253  		byte(len0 >> 16),
   254  		byte(len0 >> 8),
   255  		byte(len0),
   256  		byte(len1 >> 56),
   257  		byte(len1 >> 48),
   258  		byte(len1 >> 40),
   259  		byte(len1 >> 32),
   260  		byte(len1 >> 24),
   261  		byte(len1 >> 16),
   262  		byte(len1 >> 8),
   263  		byte(len1),
   264  	}
   265  }