...

Source file src/github.com/ProtonMail/go-crypto/eax/eax.go

Documentation: github.com/ProtonMail/go-crypto/eax

     1  // Copyright (C) 2019 ProtonTech AG
     2  
     3  // Package eax provides an implementation of the EAX
     4  // (encrypt-authenticate-translate) mode of operation, as described in
     5  // Bellare, Rogaway, and Wagner "THE EAX MODE OF OPERATION: A TWO-PASS
     6  // AUTHENTICATED-ENCRYPTION SCHEME OPTIMIZED FOR SIMPLICITY AND EFFICIENCY."
     7  // In FSE'04, volume 3017 of LNCS, 2004
     8  package eax
     9  
    10  import (
    11  	"crypto/cipher"
    12  	"crypto/subtle"
    13  	"errors"
    14  	"github.com/ProtonMail/go-crypto/internal/byteutil"
    15  )
    16  
    17  const (
    18  	defaultTagSize   = 16
    19  	defaultNonceSize = 16
    20  )
    21  
    22  type eax struct {
    23  	block     cipher.Block // Only AES-{128, 192, 256} supported
    24  	tagSize   int          // At least 12 bytes recommended
    25  	nonceSize int
    26  }
    27  
    28  func (e *eax) NonceSize() int {
    29  	return e.nonceSize
    30  }
    31  
    32  func (e *eax) Overhead() int {
    33  	return e.tagSize
    34  }
    35  
    36  // NewEAX returns an EAX instance with AES-{KEYLENGTH} and default nonce and
    37  // tag lengths. Supports {128, 192, 256}- bit key length.
    38  func NewEAX(block cipher.Block) (cipher.AEAD, error) {
    39  	return NewEAXWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize)
    40  }
    41  
    42  // NewEAXWithNonceAndTagSize returns an EAX instance with AES-{keyLength} and
    43  // given nonce and tag lengths in bytes. Panics on zero nonceSize and
    44  // exceedingly long tags.
    45  //
    46  // It is recommended to use at least 12 bytes as tag length (see, for instance,
    47  // NIST SP 800-38D).
    48  //
    49  // Only to be used for compatibility with existing cryptosystems with
    50  // non-standard parameters. For all other cases, prefer NewEAX.
    51  func NewEAXWithNonceAndTagSize(
    52  	block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
    53  	if nonceSize < 1 {
    54  		return nil, eaxError("Cannot initialize EAX with nonceSize = 0")
    55  	}
    56  	if tagSize > block.BlockSize() {
    57  		return nil, eaxError("Custom tag length exceeds blocksize")
    58  	}
    59  	return &eax{
    60  		block:     block,
    61  		tagSize:   tagSize,
    62  		nonceSize: nonceSize,
    63  	}, nil
    64  }
    65  
    66  func (e *eax) Seal(dst, nonce, plaintext, adata []byte) []byte {
    67  	if len(nonce) > e.nonceSize {
    68  		panic("crypto/eax: Nonce too long for this instance")
    69  	}
    70  	ret, out := byteutil.SliceForAppend(dst, len(plaintext)+e.tagSize)
    71  	omacNonce := e.omacT(0, nonce)
    72  	omacAdata := e.omacT(1, adata)
    73  
    74  	// Encrypt message using CTR mode and omacNonce as IV
    75  	ctr := cipher.NewCTR(e.block, omacNonce)
    76  	ciphertextData := out[:len(plaintext)]
    77  	ctr.XORKeyStream(ciphertextData, plaintext)
    78  
    79  	omacCiphertext := e.omacT(2, ciphertextData)
    80  
    81  	tag := out[len(plaintext):]
    82  	for i := 0; i < e.tagSize; i++ {
    83  		tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i]
    84  	}
    85  	return ret
    86  }
    87  
    88  func (e *eax) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
    89  	if len(nonce) > e.nonceSize {
    90  		panic("crypto/eax: Nonce too long for this instance")
    91  	}
    92  	if len(ciphertext) < e.tagSize {
    93  		return nil, eaxError("Ciphertext shorter than tag length")
    94  	}
    95  	sep := len(ciphertext) - e.tagSize
    96  
    97  	// Compute tag
    98  	omacNonce := e.omacT(0, nonce)
    99  	omacAdata := e.omacT(1, adata)
   100  	omacCiphertext := e.omacT(2, ciphertext[:sep])
   101  
   102  	tag := make([]byte, e.tagSize)
   103  	for i := 0; i < e.tagSize; i++ {
   104  		tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i]
   105  	}
   106  
   107  	// Compare tags
   108  	if subtle.ConstantTimeCompare(ciphertext[sep:], tag) != 1 {
   109  		return nil, eaxError("Tag authentication failed")
   110  	}
   111  
   112  	// Decrypt ciphertext
   113  	ret, out := byteutil.SliceForAppend(dst, len(ciphertext))
   114  	ctr := cipher.NewCTR(e.block, omacNonce)
   115  	ctr.XORKeyStream(out, ciphertext[:sep])
   116  
   117  	return ret[:sep], nil
   118  }
   119  
   120  // Tweakable OMAC - Calls OMAC_K([t]_n || plaintext)
   121  func (e *eax) omacT(t byte, plaintext []byte) []byte {
   122  	blockSize := e.block.BlockSize()
   123  	byteT := make([]byte, blockSize)
   124  	byteT[blockSize-1] = t
   125  	concat := append(byteT, plaintext...)
   126  	return e.omac(concat)
   127  }
   128  
   129  func (e *eax) omac(plaintext []byte) []byte {
   130  	blockSize := e.block.BlockSize()
   131  	// L ← E_K(0^n); B ← 2L; P ← 4L
   132  	L := make([]byte, blockSize)
   133  	e.block.Encrypt(L, L)
   134  	B := byteutil.GfnDouble(L)
   135  	P := byteutil.GfnDouble(B)
   136  
   137  	// CBC with IV = 0
   138  	cbc := cipher.NewCBCEncrypter(e.block, make([]byte, blockSize))
   139  	padded := e.pad(plaintext, B, P)
   140  	cbcCiphertext := make([]byte, len(padded))
   141  	cbc.CryptBlocks(cbcCiphertext, padded)
   142  
   143  	return cbcCiphertext[len(cbcCiphertext)-blockSize:]
   144  }
   145  
   146  func (e *eax) pad(plaintext, B, P []byte) []byte {
   147  	// if |M| in {n, 2n, 3n, ...}
   148  	blockSize := e.block.BlockSize()
   149  	if len(plaintext) != 0 && len(plaintext)%blockSize == 0 {
   150  		return byteutil.RightXor(plaintext, B)
   151  	}
   152  
   153  	// else return (M || 1 || 0^(n−1−(|M| % n))) xor→ P
   154  	ending := make([]byte, blockSize-len(plaintext)%blockSize)
   155  	ending[0] = 0x80
   156  	padded := append(plaintext, ending...)
   157  	return byteutil.RightXor(padded, P)
   158  }
   159  
   160  func eaxError(err string) error {
   161  	return errors.New("crypto/eax: " + err)
   162  }
   163  

View as plain text