| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package gcm |
| |
| import ( |
| "crypto/internal/fips140" |
| "crypto/internal/fips140/aes" |
| "crypto/internal/fips140/alias" |
| "errors" |
| ) |
| |
| // GCM represents a Galois Counter Mode with a specific key. |
| type GCM struct { |
| cipher aes.Block |
| nonceSize int |
| tagSize int |
| gcmPlatformData |
| } |
| |
| func New(cipher *aes.Block, nonceSize, tagSize int) (*GCM, error) { |
| // This function is outlined to let the allocation happen on the parent stack. |
| return newGCM(&GCM{}, cipher, nonceSize, tagSize) |
| } |
| |
| // newGCM is marked go:noinline to avoid it inlining into New, and making New |
| // too complex to inline itself. |
| // |
| //go:noinline |
| func newGCM(g *GCM, cipher *aes.Block, nonceSize, tagSize int) (*GCM, error) { |
| if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize { |
| return nil, errors.New("cipher: incorrect tag size given to GCM") |
| } |
| if nonceSize <= 0 { |
| return nil, errors.New("cipher: the nonce can't have zero length") |
| } |
| if cipher.BlockSize() != gcmBlockSize { |
| return nil, errors.New("cipher: NewGCM requires 128-bit block cipher") |
| } |
| g.cipher = *cipher |
| g.nonceSize = nonceSize |
| g.tagSize = tagSize |
| initGCM(g) |
| return g, nil |
| } |
| |
| const ( |
| gcmBlockSize = 16 |
| gcmTagSize = 16 |
| gcmMinimumTagSize = 12 // NIST SP 800-38D recommends tags with 12 or more bytes. |
| gcmStandardNonceSize = 12 |
| ) |
| |
| func (g *GCM) NonceSize() int { |
| return g.nonceSize |
| } |
| |
| func (g *GCM) Overhead() int { |
| return g.tagSize |
| } |
| |
| func (g *GCM) Seal(dst, nonce, plaintext, data []byte) []byte { |
| fips140.RecordNonApproved() |
| return g.sealAfterIndicator(dst, nonce, plaintext, data) |
| } |
| |
| func (g *GCM) sealAfterIndicator(dst, nonce, plaintext, data []byte) []byte { |
| if len(nonce) != g.nonceSize { |
| panic("crypto/cipher: incorrect nonce length given to GCM") |
| } |
| if g.nonceSize == 0 { |
| panic("crypto/cipher: incorrect GCM nonce size") |
| } |
| if uint64(len(plaintext)) > uint64((1<<32)-2)*gcmBlockSize { |
| panic("crypto/cipher: message too large for GCM") |
| } |
| |
| ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize) |
| if alias.InexactOverlap(out, plaintext) { |
| panic("crypto/cipher: invalid buffer overlap of output and input") |
| } |
| if alias.AnyOverlap(out, data) { |
| panic("crypto/cipher: invalid buffer overlap of output and additional data") |
| } |
| |
| seal(out, g, nonce, plaintext, data) |
| return ret |
| } |
| |
| var errOpen = errors.New("cipher: message authentication failed") |
| |
| func (g *GCM) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { |
| if len(nonce) != g.nonceSize { |
| panic("crypto/cipher: incorrect nonce length given to GCM") |
| } |
| // Sanity check to prevent the authentication from always succeeding if an |
| // implementation leaves tagSize uninitialized, for example. |
| if g.tagSize < gcmMinimumTagSize { |
| panic("crypto/cipher: incorrect GCM tag size") |
| } |
| |
| if len(ciphertext) < g.tagSize { |
| return nil, errOpen |
| } |
| if uint64(len(ciphertext)) > uint64((1<<32)-2)*gcmBlockSize+uint64(g.tagSize) { |
| return nil, errOpen |
| } |
| |
| ret, out := sliceForAppend(dst, len(ciphertext)-g.tagSize) |
| if alias.InexactOverlap(out, ciphertext) { |
| panic("crypto/cipher: invalid buffer overlap of output and input") |
| } |
| if alias.AnyOverlap(out, data) { |
| panic("crypto/cipher: invalid buffer overlap of output and additional data") |
| } |
| |
| fips140.RecordApproved() |
| if err := open(out, g, nonce, ciphertext, data); err != nil { |
| // We sometimes decrypt and authenticate concurrently, so we overwrite |
| // dst in the event of a tag mismatch. To be consistent across platforms |
| // and to avoid releasing unauthenticated plaintext, we clear the buffer |
| // in the event of an error. |
| clear(out) |
| return nil, err |
| } |
| return ret, nil |
| } |
| |
| // sliceForAppend takes a slice and a requested number of bytes. It returns a |
| // slice with the contents of the given slice followed by that many bytes and a |
| // second slice that aliases into it and contains only the extra bytes. If the |
| // original slice has sufficient capacity then no allocation is performed. |
| func sliceForAppend(in []byte, n int) (head, tail []byte) { |
| if total := len(in) + n; cap(in) >= total { |
| head = in[:total] |
| } else { |
| head = make([]byte, total) |
| copy(head, in) |
| } |
| tail = head[len(in):] |
| return |
| } |