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
     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  func counterCryptASM(nr int, out, in []byte, counter *[gcmBlockSize]byte, key *uint32)
    55  
    56  // NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
    57  // called by [crypto/cipher.NewGCM] via the gcmAble interface.
    58  func (c *aesCipherAsm) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) {
    59  	var h1, h2 uint64
    60  	g := &gcmAsm{cipher: c, ks: c.enc, nonceSize: nonceSize, tagSize: tagSize}
    61  
    62  	hle := make([]byte, gcmBlockSize)
    63  
    64  	c.Encrypt(hle, hle)
    65  
    66  	// Reverse the bytes in each 8 byte chunk
    67  	// Load little endian, store big endian
    68  	if runtime.GOARCH == "ppc64le" {
    69  		h1 = binary.LittleEndian.Uint64(hle[:8])
    70  		h2 = binary.LittleEndian.Uint64(hle[8:])
    71  	} else {
    72  		h1 = binary.BigEndian.Uint64(hle[:8])
    73  		h2 = binary.BigEndian.Uint64(hle[8:])
    74  	}
    75  	binary.BigEndian.PutUint64(hle[:8], h1)
    76  	binary.BigEndian.PutUint64(hle[8:], h2)
    77  	gcmInit(&g.productTable, hle)
    78  
    79  	return g, nil
    80  }
    81  
    82  func (g *gcmAsm) NonceSize() int {
    83  	return g.nonceSize
    84  }
    85  
    86  func (g *gcmAsm) Overhead() int {
    87  	return g.tagSize
    88  }
    89  
    90  func sliceForAppend(in []byte, n int) (head, tail []byte) {
    91  	if total := len(in) + n; cap(in) >= total {
    92  		head = in[:total]
    93  	} else {
    94  		head = make([]byte, total)
    95  		copy(head, in)
    96  	}
    97  	tail = head[len(in):]
    98  	return
    99  }
   100  
   101  // deriveCounter computes the initial GCM counter state from the given nonce.
   102  func (g *gcmAsm) deriveCounter(counter *[gcmBlockSize]byte, nonce []byte) {
   103  	if len(nonce) == gcmStandardNonceSize {
   104  		copy(counter[:], nonce)
   105  		counter[gcmBlockSize-1] = 1
   106  	} else {
   107  		var hash [16]byte
   108  		g.paddedGHASH(&hash, nonce)
   109  		lens := gcmLengths(0, uint64(len(nonce))*8)
   110  		g.paddedGHASH(&hash, lens[:])
   111  		copy(counter[:], hash[:])
   112  	}
   113  }
   114  
   115  // counterCrypt encrypts in using AES in counter mode and places the result
   116  // into out. counter is the initial count value and will be updated with the next
   117  // count value. The length of out must be greater than or equal to the length
   118  // of in.
   119  // counterCryptASM implements counterCrypt which then allows the loop to
   120  // be unrolled and optimized.
   121  func (g *gcmAsm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
   122  	counterCryptASM(len(g.cipher.enc)/4-1, out, in, counter, &g.cipher.enc[0])
   123  }
   124  
   125  // increments the rightmost 32-bits of the count value by 1.
   126  func gcmInc32(counterBlock *[16]byte) {
   127  	c := counterBlock[len(counterBlock)-4:]
   128  	x := binary.BigEndian.Uint32(c) + 1
   129  	binary.BigEndian.PutUint32(c, x)
   130  }
   131  
   132  // paddedGHASH pads data with zeroes until its length is a multiple of
   133  // 16-bytes. It then calculates a new value for hash using the ghash
   134  // algorithm.
   135  func (g *gcmAsm) paddedGHASH(hash *[16]byte, data []byte) {
   136  	if siz := len(data) - (len(data) % gcmBlockSize); siz > 0 {
   137  		gcmHash(hash[:], &g.productTable, data[:], siz)
   138  		data = data[siz:]
   139  	}
   140  	if len(data) > 0 {
   141  		var s [16]byte
   142  		copy(s[:], data)
   143  		gcmHash(hash[:], &g.productTable, s[:], len(s))
   144  	}
   145  }
   146  
   147  // auth calculates GHASH(ciphertext, additionalData), masks the result with
   148  // tagMask and writes the result to out.
   149  func (g *gcmAsm) auth(out, ciphertext, aad []byte, tagMask *[gcmTagSize]byte) {
   150  	var hash [16]byte
   151  	g.paddedGHASH(&hash, aad)
   152  	g.paddedGHASH(&hash, ciphertext)
   153  	lens := gcmLengths(uint64(len(aad))*8, uint64(len(ciphertext))*8)
   154  	g.paddedGHASH(&hash, lens[:])
   155  
   156  	copy(out, hash[:])
   157  	for i := range out {
   158  		out[i] ^= tagMask[i]
   159  	}
   160  }
   161  
   162  // Seal encrypts and authenticates plaintext. See the [cipher.AEAD] interface for
   163  // details.
   164  func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
   165  	if len(nonce) != g.nonceSize {
   166  		panic("cipher: incorrect nonce length given to GCM")
   167  	}
   168  	if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize {
   169  		panic("cipher: message too large for GCM")
   170  	}
   171  
   172  	ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize)
   173  
   174  	var counter, tagMask [gcmBlockSize]byte
   175  	g.deriveCounter(&counter, nonce)
   176  
   177  	g.cipher.Encrypt(tagMask[:], counter[:])
   178  	gcmInc32(&counter)
   179  
   180  	g.counterCrypt(out, plaintext, &counter)
   181  	g.auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask)
   182  
   183  	return ret
   184  }
   185  
   186  // Open authenticates and decrypts ciphertext. See the [cipher.AEAD] interface
   187  // for details.
   188  func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
   189  	if len(nonce) != g.nonceSize {
   190  		panic("cipher: incorrect nonce length given to GCM")
   191  	}
   192  	if len(ciphertext) < g.tagSize {
   193  		return nil, errOpen
   194  	}
   195  	if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(BlockSize)+uint64(g.tagSize) {
   196  		return nil, errOpen
   197  	}
   198  
   199  	tag := ciphertext[len(ciphertext)-g.tagSize:]
   200  	ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
   201  
   202  	var counter, tagMask [gcmBlockSize]byte
   203  	g.deriveCounter(&counter, nonce)
   204  
   205  	g.cipher.Encrypt(tagMask[:], counter[:])
   206  	gcmInc32(&counter)
   207  
   208  	var expectedTag [gcmTagSize]byte
   209  	g.auth(expectedTag[:], ciphertext, data, &tagMask)
   210  
   211  	ret, out := sliceForAppend(dst, len(ciphertext))
   212  
   213  	if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
   214  		for i := range out {
   215  			out[i] = 0
   216  		}
   217  		return nil, errOpen
   218  	}
   219  
   220  	g.counterCrypt(out, ciphertext, &counter)
   221  	return ret, nil
   222  }
   223  
   224  func gcmLengths(len0, len1 uint64) [16]byte {
   225  	return [16]byte{
   226  		byte(len0 >> 56),
   227  		byte(len0 >> 48),
   228  		byte(len0 >> 40),
   229  		byte(len0 >> 32),
   230  		byte(len0 >> 24),
   231  		byte(len0 >> 16),
   232  		byte(len0 >> 8),
   233  		byte(len0),
   234  		byte(len1 >> 56),
   235  		byte(len1 >> 48),
   236  		byte(len1 >> 40),
   237  		byte(len1 >> 32),
   238  		byte(len1 >> 24),
   239  		byte(len1 >> 16),
   240  		byte(len1 >> 8),
   241  		byte(len1),
   242  	}
   243  }
   244  

View as plain text