github.com/mad-day/Yawning-crypto@v0.0.0-20190711051033-5a5f8cca32ec/bsaes/internal/modes/gcm.go (about)

     1  // Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
     2  // Copyright (c) 2017 Yawning Angel <yawning at schwanenlied dot me>
     3  //
     4  // Permission is hereby granted, free of charge, to any person obtaining
     5  // a copy of this software and associated documentation files (the
     6  // "Software"), to deal in the Software without restriction, including
     7  // without limitation the rights to use, copy, modify, merge, publish,
     8  // distribute, sublicense, and/or sell copies of the Software, and to
     9  // permit persons to whom the Software is furnished to do so, subject to
    10  // the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be
    13  // included in all copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    16  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    17  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    18  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
    19  // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
    20  // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    21  // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    22  // SOFTWARE.
    23  
    24  package modes
    25  
    26  import (
    27  	"crypto/cipher"
    28  	"crypto/subtle"
    29  	"encoding/binary"
    30  	"errors"
    31  
    32  	"github.com/mad-day/Yawning-crypto/bsaes/ghash"
    33  )
    34  
    35  const (
    36  	gcmNonceSize = 96 / 8
    37  	gcmTagSize   = 16
    38  )
    39  
    40  func (m *BlockModesImpl) NewGCM(size int) (cipher.AEAD, error) {
    41  	ecb := m.b.(bulkECBAble)
    42  	if ecb.BlockSize() != blockSize {
    43  		return nil, errors.New("bsaes/NewGCM: GCM requires 128 bit block sizes")
    44  	}
    45  
    46  	return newGCMImpl(ecb, size), nil
    47  }
    48  
    49  type gcmImpl struct {
    50  	ecb bulkECBAble
    51  
    52  	nonceSize int
    53  	stride    int
    54  }
    55  
    56  func (g *gcmImpl) NonceSize() int {
    57  	return g.nonceSize
    58  }
    59  
    60  func (g *gcmImpl) Overhead() int {
    61  	return gcmTagSize
    62  }
    63  
    64  func (g *gcmImpl) deriveNonceVals(h, j, preCounterBlock *[blockSize]byte, nonce []byte) {
    65  	g.ecb.Encrypt(h[:], h[:])
    66  	if len(nonce) == gcmNonceSize {
    67  		copy(j[:], nonce[:gcmNonceSize])
    68  		j[blockSize-1] = 1
    69  	} else {
    70  		var p [blockSize]byte
    71  		ghash.Ghash(j, h, nonce)
    72  		binary.BigEndian.PutUint32(p[12:], uint32(len(nonce))<<3)
    73  		ghash.Ghash(j, h, p[:])
    74  	}
    75  	g.ecb.Encrypt(preCounterBlock[:], j[:])
    76  }
    77  
    78  func (g *gcmImpl) gctr(iv *[blockSize]byte, dst, src []byte) {
    79  	idx := g.stride * blockSize
    80  	buf := make([]byte, g.stride*blockSize)
    81  	inc32(iv)
    82  
    83  	for len(src) > 0 {
    84  		if idx >= len(buf) {
    85  			for i := 0; i < g.stride; i++ {
    86  				copy(buf[i*blockSize:], iv[:])
    87  				inc32(iv)
    88  			}
    89  			g.ecb.BulkEncrypt(buf, buf)
    90  			idx = 0
    91  		}
    92  
    93  		n := len(buf) - idx
    94  		if sLen := len(src); sLen < n {
    95  			n = sLen
    96  		}
    97  		for i, v := range src[:n] {
    98  			dst[i] = v ^ buf[idx+i]
    99  		}
   100  
   101  		dst, src = dst[n:], src[n:]
   102  		idx += n
   103  	}
   104  
   105  	for i := range buf {
   106  		buf[i] = 0
   107  	}
   108  }
   109  
   110  func (g *gcmImpl) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
   111  	if len(nonce) != g.nonceSize {
   112  		panic("bsaes/gcmImpl.Seal: nonce with invalid size provided")
   113  	}
   114  
   115  	// Yes, this always allocates.  It makes life easier.
   116  	sz := len(plaintext)
   117  	if uint64(sz) > 0xfffffffe0 { // len(P) <= 2^39 - 256 (bits)
   118  		panic("bsaes/gcmImpl.Seal: plaintext too large")
   119  	}
   120  	out := make([]byte, sz+gcmTagSize)
   121  
   122  	// Define H, block J0, and the pre-counter block.
   123  	var h, j, preCounterBlock [blockSize]byte
   124  	g.deriveNonceVals(&h, &j, &preCounterBlock, nonce)
   125  
   126  	// Let C=GCTR K(inc32(J0), P).
   127  	g.gctr(&j, out, plaintext)
   128  
   129  	// S = GHASH H (A || 0 v || C || 0 u || [len(A)] 64 || [len(C)] 64).
   130  	var s, p [blockSize]byte
   131  	ghash.Ghash(&s, &h, additionalData)
   132  	ghash.Ghash(&s, &h, out[:sz])
   133  	binary.BigEndian.PutUint32(p[4:], uint32(len(additionalData))<<3)
   134  	binary.BigEndian.PutUint32(p[12:], uint32(sz)<<3)
   135  	ghash.Ghash(&s, &h, p[:])
   136  
   137  	// Let T = MSB t(GCTR K(J0, S))
   138  	for i, v := range preCounterBlock {
   139  		out[sz+i] = s[i] ^ v
   140  	}
   141  
   142  	dst = append(dst, out...)
   143  	return dst
   144  }
   145  
   146  var errFail = errors.New("cipher: message authentication failed")
   147  
   148  func (g *gcmImpl) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
   149  	if len(nonce) != g.nonceSize {
   150  		panic("bsaes/gcmImpl.Seal: nonce with invalid size provided")
   151  	}
   152  
   153  	sz := len(ciphertext)
   154  	if sz < gcmTagSize {
   155  		return nil, errFail
   156  	}
   157  	sz -= gcmTagSize
   158  	if uint64(sz) > 0xfffffffe0 {
   159  		return nil, errFail
   160  	}
   161  
   162  	// Define H, block J0, and the pre-counter block.
   163  	var h, j, preCounterBlock [blockSize]byte
   164  	g.deriveNonceVals(&h, &j, &preCounterBlock, nonce)
   165  
   166  	// S = GHASH H (A || 0 v || C || 0 u || [len(A)] 64 || [len(C)] 64).
   167  	var s, p [blockSize]byte
   168  	ghash.Ghash(&s, &h, additionalData)
   169  	ghash.Ghash(&s, &h, ciphertext[:sz])
   170  	binary.BigEndian.PutUint32(p[4:], uint32(len(additionalData))<<3)
   171  	binary.BigEndian.PutUint32(p[12:], uint32(sz)<<3)
   172  	ghash.Ghash(&s, &h, p[:])
   173  	for i, v := range preCounterBlock {
   174  		s[i] ^= v
   175  	}
   176  
   177  	if subtle.ConstantTimeCompare(s[:], ciphertext[sz:]) != 1 {
   178  		return nil, errFail
   179  	}
   180  
   181  	out := make([]byte, sz)
   182  	g.gctr(&j, out, ciphertext[:sz])
   183  	dst = append(dst, out...)
   184  
   185  	return dst, nil
   186  }
   187  
   188  func inc32(ctr *[blockSize]byte) {
   189  	v := binary.BigEndian.Uint32(ctr[12:]) + 1
   190  	binary.BigEndian.PutUint32(ctr[12:], v)
   191  }
   192  
   193  func newGCMImpl(ecb bulkECBAble, size int) cipher.AEAD {
   194  	g := new(gcmImpl)
   195  	g.ecb = ecb
   196  	g.nonceSize = size
   197  	g.stride = g.ecb.Stride()
   198  	return g
   199  }