Source file src/crypto/aes/gcm_ppc64le.go

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

View as plain text