Source file src/crypto/internal/boring/aes.go

     1  // Copyright 2017 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 boringcrypto && linux && (amd64 || arm64) && !android && !msan
     6  
     7  package boring
     8  
     9  /*
    10  
    11  #include "goboringcrypto.h"
    12  
    13  // These wrappers allocate out_len on the C stack, and check that it matches the expected
    14  // value, to avoid having to pass a pointer from Go, which would escape to the heap.
    15  
    16  int EVP_AEAD_CTX_seal_wrapper(const GO_EVP_AEAD_CTX *ctx, uint8_t *out,
    17  							  size_t exp_out_len,
    18  							  const uint8_t *nonce, size_t nonce_len,
    19  							  const uint8_t *in, size_t in_len,
    20  							  const uint8_t *ad, size_t ad_len) {
    21  	size_t out_len;
    22  	int ok = _goboringcrypto_EVP_AEAD_CTX_seal(ctx, out, &out_len, exp_out_len,
    23  		nonce, nonce_len, in, in_len, ad, ad_len);
    24  	if (out_len != exp_out_len) {
    25  		return 0;
    26  	}
    27  	return ok;
    28  };
    29  
    30  int EVP_AEAD_CTX_open_wrapper(const GO_EVP_AEAD_CTX *ctx, uint8_t *out,
    31  							  size_t exp_out_len,
    32  							  const uint8_t *nonce, size_t nonce_len,
    33  							  const uint8_t *in, size_t in_len,
    34  							  const uint8_t *ad, size_t ad_len) {
    35  	size_t out_len;
    36  	int ok = _goboringcrypto_EVP_AEAD_CTX_open(ctx, out, &out_len, exp_out_len,
    37  		nonce, nonce_len, in, in_len, ad, ad_len);
    38  	if (out_len != exp_out_len) {
    39  		return 0;
    40  	}
    41  	return ok;
    42  };
    43  
    44  */
    45  import "C"
    46  import (
    47  	"bytes"
    48  	"crypto/cipher"
    49  	"errors"
    50  	"runtime"
    51  	"slices"
    52  	"strconv"
    53  	"unsafe"
    54  )
    55  
    56  type aesKeySizeError int
    57  
    58  func (k aesKeySizeError) Error() string {
    59  	return "crypto/aes: invalid key size " + strconv.Itoa(int(k))
    60  }
    61  
    62  const aesBlockSize = 16
    63  
    64  type aesCipher struct {
    65  	key []byte
    66  	enc C.GO_AES_KEY
    67  	dec C.GO_AES_KEY
    68  }
    69  
    70  type extraModes interface {
    71  	// Copied out of crypto/aes/modes.go.
    72  	NewCBCEncrypter(iv []byte) cipher.BlockMode
    73  	NewCBCDecrypter(iv []byte) cipher.BlockMode
    74  	NewCTR(iv []byte) cipher.Stream
    75  	NewGCM(nonceSize, tagSize int) (cipher.AEAD, error)
    76  }
    77  
    78  var _ extraModes = (*aesCipher)(nil)
    79  
    80  func NewAESCipher(key []byte) (cipher.Block, error) {
    81  	c := &aesCipher{key: bytes.Clone(key)}
    82  	// Note: 0 is success, contradicting the usual BoringCrypto convention.
    83  	if C._goboringcrypto_AES_set_decrypt_key((*C.uint8_t)(unsafe.Pointer(&c.key[0])), C.uint(8*len(c.key)), &c.dec) != 0 ||
    84  		C._goboringcrypto_AES_set_encrypt_key((*C.uint8_t)(unsafe.Pointer(&c.key[0])), C.uint(8*len(c.key)), &c.enc) != 0 {
    85  		return nil, aesKeySizeError(len(key))
    86  	}
    87  	return c, nil
    88  }
    89  
    90  func (c *aesCipher) BlockSize() int { return aesBlockSize }
    91  
    92  func (c *aesCipher) Encrypt(dst, src []byte) {
    93  	if inexactOverlap(dst, src) {
    94  		panic("crypto/cipher: invalid buffer overlap")
    95  	}
    96  	if len(src) < aesBlockSize {
    97  		panic("crypto/aes: input not full block")
    98  	}
    99  	if len(dst) < aesBlockSize {
   100  		panic("crypto/aes: output not full block")
   101  	}
   102  	C._goboringcrypto_AES_encrypt(
   103  		(*C.uint8_t)(unsafe.Pointer(&src[0])),
   104  		(*C.uint8_t)(unsafe.Pointer(&dst[0])),
   105  		&c.enc)
   106  }
   107  
   108  func (c *aesCipher) Decrypt(dst, src []byte) {
   109  	if inexactOverlap(dst, src) {
   110  		panic("crypto/cipher: invalid buffer overlap")
   111  	}
   112  	if len(src) < aesBlockSize {
   113  		panic("crypto/aes: input not full block")
   114  	}
   115  	if len(dst) < aesBlockSize {
   116  		panic("crypto/aes: output not full block")
   117  	}
   118  	C._goboringcrypto_AES_decrypt(
   119  		(*C.uint8_t)(unsafe.Pointer(&src[0])),
   120  		(*C.uint8_t)(unsafe.Pointer(&dst[0])),
   121  		&c.dec)
   122  }
   123  
   124  type aesCBC struct {
   125  	key  *C.GO_AES_KEY
   126  	mode C.int
   127  	iv   [aesBlockSize]byte
   128  }
   129  
   130  func (x *aesCBC) BlockSize() int { return aesBlockSize }
   131  
   132  func (x *aesCBC) CryptBlocks(dst, src []byte) {
   133  	if inexactOverlap(dst, src) {
   134  		panic("crypto/cipher: invalid buffer overlap")
   135  	}
   136  	if len(src)%aesBlockSize != 0 {
   137  		panic("crypto/cipher: input not full blocks")
   138  	}
   139  	if len(dst) < len(src) {
   140  		panic("crypto/cipher: output smaller than input")
   141  	}
   142  	if len(src) > 0 {
   143  		C._goboringcrypto_AES_cbc_encrypt(
   144  			(*C.uint8_t)(unsafe.Pointer(&src[0])),
   145  			(*C.uint8_t)(unsafe.Pointer(&dst[0])),
   146  			C.size_t(len(src)), x.key,
   147  			(*C.uint8_t)(unsafe.Pointer(&x.iv[0])), x.mode)
   148  	}
   149  }
   150  
   151  func (x *aesCBC) SetIV(iv []byte) {
   152  	if len(iv) != aesBlockSize {
   153  		panic("cipher: incorrect length IV")
   154  	}
   155  	copy(x.iv[:], iv)
   156  }
   157  
   158  func (c *aesCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode {
   159  	x := &aesCBC{key: &c.enc, mode: C.GO_AES_ENCRYPT}
   160  	copy(x.iv[:], iv)
   161  	return x
   162  }
   163  
   164  func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode {
   165  	x := &aesCBC{key: &c.dec, mode: C.GO_AES_DECRYPT}
   166  	copy(x.iv[:], iv)
   167  	return x
   168  }
   169  
   170  type aesCTR struct {
   171  	key        *C.GO_AES_KEY
   172  	iv         [aesBlockSize]byte
   173  	num        C.uint
   174  	ecount_buf [16]C.uint8_t
   175  }
   176  
   177  func (x *aesCTR) XORKeyStream(dst, src []byte) {
   178  	if inexactOverlap(dst, src) {
   179  		panic("crypto/cipher: invalid buffer overlap")
   180  	}
   181  	if len(dst) < len(src) {
   182  		panic("crypto/cipher: output smaller than input")
   183  	}
   184  	if len(src) == 0 {
   185  		return
   186  	}
   187  	C._goboringcrypto_AES_ctr128_encrypt(
   188  		(*C.uint8_t)(unsafe.Pointer(&src[0])),
   189  		(*C.uint8_t)(unsafe.Pointer(&dst[0])),
   190  		C.size_t(len(src)), x.key, (*C.uint8_t)(unsafe.Pointer(&x.iv[0])),
   191  		&x.ecount_buf[0], &x.num)
   192  }
   193  
   194  func (c *aesCipher) NewCTR(iv []byte) cipher.Stream {
   195  	x := &aesCTR{key: &c.enc}
   196  	copy(x.iv[:], iv)
   197  	return x
   198  }
   199  
   200  type aesGCM struct {
   201  	ctx  C.GO_EVP_AEAD_CTX
   202  	aead *C.GO_EVP_AEAD
   203  }
   204  
   205  const (
   206  	gcmBlockSize         = 16
   207  	gcmTagSize           = 16
   208  	gcmStandardNonceSize = 12
   209  )
   210  
   211  type aesNonceSizeError int
   212  
   213  func (n aesNonceSizeError) Error() string {
   214  	return "crypto/aes: invalid GCM nonce size " + strconv.Itoa(int(n))
   215  }
   216  
   217  type noGCM struct {
   218  	cipher.Block
   219  }
   220  
   221  func (c *aesCipher) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) {
   222  	if nonceSize != gcmStandardNonceSize && tagSize != gcmTagSize {
   223  		return nil, errors.New("crypto/aes: GCM tag and nonce sizes can't be non-standard at the same time")
   224  	}
   225  	// Fall back to standard library for GCM with non-standard nonce or tag size.
   226  	if nonceSize != gcmStandardNonceSize {
   227  		return cipher.NewGCMWithNonceSize(&noGCM{c}, nonceSize)
   228  	}
   229  	if tagSize != gcmTagSize {
   230  		return cipher.NewGCMWithTagSize(&noGCM{c}, tagSize)
   231  	}
   232  	return c.newGCM(0)
   233  }
   234  
   235  const (
   236  	VersionTLS12 = 0x0303
   237  	VersionTLS13 = 0x0304
   238  )
   239  
   240  func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) {
   241  	return c.(*aesCipher).newGCM(VersionTLS12)
   242  }
   243  
   244  func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) {
   245  	return c.(*aesCipher).newGCM(VersionTLS13)
   246  }
   247  
   248  func (c *aesCipher) newGCM(tlsVersion uint16) (cipher.AEAD, error) {
   249  	var aead *C.GO_EVP_AEAD
   250  	switch len(c.key) * 8 {
   251  	case 128:
   252  		switch tlsVersion {
   253  		case VersionTLS12:
   254  			aead = C._goboringcrypto_EVP_aead_aes_128_gcm_tls12()
   255  		case VersionTLS13:
   256  			aead = C._goboringcrypto_EVP_aead_aes_128_gcm_tls13()
   257  		default:
   258  			aead = C._goboringcrypto_EVP_aead_aes_128_gcm()
   259  		}
   260  	case 256:
   261  		switch tlsVersion {
   262  		case VersionTLS12:
   263  			aead = C._goboringcrypto_EVP_aead_aes_256_gcm_tls12()
   264  		case VersionTLS13:
   265  			aead = C._goboringcrypto_EVP_aead_aes_256_gcm_tls13()
   266  		default:
   267  			aead = C._goboringcrypto_EVP_aead_aes_256_gcm()
   268  		}
   269  	default:
   270  		// Fall back to standard library for GCM with non-standard key size.
   271  		return cipher.NewGCMWithNonceSize(&noGCM{c}, gcmStandardNonceSize)
   272  	}
   273  
   274  	g := &aesGCM{aead: aead}
   275  	if C._goboringcrypto_EVP_AEAD_CTX_init(&g.ctx, aead, (*C.uint8_t)(unsafe.Pointer(&c.key[0])), C.size_t(len(c.key)), C.GO_EVP_AEAD_DEFAULT_TAG_LENGTH, nil) == 0 {
   276  		return nil, fail("EVP_AEAD_CTX_init")
   277  	}
   278  	// Note: Because of the finalizer, any time g.ctx is passed to cgo,
   279  	// that call must be followed by a call to runtime.KeepAlive(g),
   280  	// to make sure g is not collected (and finalized) before the cgo
   281  	// call returns.
   282  	runtime.SetFinalizer(g, (*aesGCM).finalize)
   283  	if g.NonceSize() != gcmStandardNonceSize {
   284  		panic("boringcrypto: internal confusion about nonce size")
   285  	}
   286  	if g.Overhead() != gcmTagSize {
   287  		panic("boringcrypto: internal confusion about tag size")
   288  	}
   289  
   290  	return g, nil
   291  }
   292  
   293  func (g *aesGCM) finalize() {
   294  	C._goboringcrypto_EVP_AEAD_CTX_cleanup(&g.ctx)
   295  }
   296  
   297  func (g *aesGCM) NonceSize() int {
   298  	return int(C._goboringcrypto_EVP_AEAD_nonce_length(g.aead))
   299  }
   300  
   301  func (g *aesGCM) Overhead() int {
   302  	return int(C._goboringcrypto_EVP_AEAD_max_overhead(g.aead))
   303  }
   304  
   305  // base returns the address of the underlying array in b,
   306  // being careful not to panic when b has zero length.
   307  func base(b []byte) *C.uint8_t {
   308  	if len(b) == 0 {
   309  		return nil
   310  	}
   311  	return (*C.uint8_t)(unsafe.Pointer(&b[0]))
   312  }
   313  
   314  func (g *aesGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
   315  	if len(nonce) != gcmStandardNonceSize {
   316  		panic("cipher: incorrect nonce length given to GCM")
   317  	}
   318  	if uint64(len(plaintext)) > ((1<<32)-2)*aesBlockSize || len(plaintext)+gcmTagSize < len(plaintext) {
   319  		panic("cipher: message too large for GCM")
   320  	}
   321  	if len(dst)+len(plaintext)+gcmTagSize < len(dst) {
   322  		panic("cipher: message too large for buffer")
   323  	}
   324  
   325  	// Make room in dst to append plaintext+overhead.
   326  	n := len(dst)
   327  	dst = slices.Grow(dst, len(plaintext)+gcmTagSize)
   328  	dst = dst[:n+len(plaintext)+gcmTagSize]
   329  
   330  	// Check delayed until now to make sure len(dst) is accurate.
   331  	if inexactOverlap(dst[n:], plaintext) {
   332  		panic("cipher: invalid buffer overlap")
   333  	}
   334  
   335  	outLen := C.size_t(len(plaintext) + gcmTagSize)
   336  	ok := C.EVP_AEAD_CTX_seal_wrapper(
   337  		&g.ctx,
   338  		(*C.uint8_t)(unsafe.Pointer(&dst[n])), outLen,
   339  		base(nonce), C.size_t(len(nonce)),
   340  		base(plaintext), C.size_t(len(plaintext)),
   341  		base(additionalData), C.size_t(len(additionalData)))
   342  	runtime.KeepAlive(g)
   343  	if ok == 0 {
   344  		panic(fail("EVP_AEAD_CTX_seal"))
   345  	}
   346  	return dst[:n+int(outLen)]
   347  }
   348  
   349  var errOpen = errors.New("cipher: message authentication failed")
   350  
   351  func (g *aesGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
   352  	if len(nonce) != gcmStandardNonceSize {
   353  		panic("cipher: incorrect nonce length given to GCM")
   354  	}
   355  	if len(ciphertext) < gcmTagSize {
   356  		return nil, errOpen
   357  	}
   358  	if uint64(len(ciphertext)) > ((1<<32)-2)*aesBlockSize+gcmTagSize {
   359  		return nil, errOpen
   360  	}
   361  
   362  	// Make room in dst to append ciphertext without tag.
   363  	n := len(dst)
   364  	for cap(dst) < n+len(ciphertext)-gcmTagSize {
   365  		dst = append(dst[:cap(dst)], 0)
   366  	}
   367  	dst = dst[:n+len(ciphertext)-gcmTagSize]
   368  
   369  	// Check delayed until now to make sure len(dst) is accurate.
   370  	if inexactOverlap(dst[n:], ciphertext) {
   371  		panic("cipher: invalid buffer overlap")
   372  	}
   373  
   374  	outLen := C.size_t(len(ciphertext) - gcmTagSize)
   375  	ok := C.EVP_AEAD_CTX_open_wrapper(
   376  		&g.ctx,
   377  		base(dst[n:]), outLen,
   378  		base(nonce), C.size_t(len(nonce)),
   379  		base(ciphertext), C.size_t(len(ciphertext)),
   380  		base(additionalData), C.size_t(len(additionalData)))
   381  	runtime.KeepAlive(g)
   382  	if ok == 0 {
   383  		return nil, errOpen
   384  	}
   385  	return dst[:n+int(outLen)], nil
   386  }
   387  
   388  func anyOverlap(x, y []byte) bool {
   389  	return len(x) > 0 && len(y) > 0 &&
   390  		uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) &&
   391  		uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1]))
   392  }
   393  
   394  func inexactOverlap(x, y []byte) bool {
   395  	if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
   396  		return false
   397  	}
   398  	return anyOverlap(x, y)
   399  }
   400  

View as plain text