...

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

Documentation: crypto/internal/boring

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

View as plain text