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

View as plain text