github.com/cloudflare/circl@v1.5.0/cipher/ascon/ascon.go (about)

     1  // Package ascon provides ASCON family of light-weight AEAD ciphers.
     2  //
     3  // This package implements Ascon128 and Ascon128a two AEAD ciphers as specified
     4  // in ASCON v1.2 by C. Dobraunig, M. Eichlseder, F. Mendel, M. Schläffer.
     5  // https://ascon.iaik.tugraz.at/index.html
     6  //
     7  // It also implements Ascon-80pq, which has an increased key-size to provide
     8  // more resistance against a quantum adversary using Grover’s algorithm for
     9  // key search. Since Ascon-128 and Ascon-80pq share the same building blocks
    10  // and same parameters except the size of the key, it is claimed the same
    11  // security for Ascon-80pq against classical attacks as for Ascon-128.
    12  package ascon
    13  
    14  import (
    15  	"crypto/subtle"
    16  	"encoding/binary"
    17  	"errors"
    18  	"math/bits"
    19  )
    20  
    21  const (
    22  	KeySize     = 16 // For Ascon128 and Ascon128a.
    23  	KeySize80pq = 20 // Only for Ascon80pq.
    24  	NonceSize   = 16
    25  	TagSize     = 16
    26  )
    27  
    28  type Mode int
    29  
    30  // KeySize is 16 for Ascon128 and Ascon128a, or 20 for Ascon80pq.
    31  func (m Mode) KeySize() int {
    32  	switch m {
    33  	case Ascon128, Ascon128a, Ascon80pq:
    34  		v := int(m) >> 2
    35  		return KeySize&^v | KeySize80pq&v
    36  	default:
    37  		panic(ErrMode)
    38  	}
    39  }
    40  
    41  func (m Mode) String() string {
    42  	switch m {
    43  	case Ascon128:
    44  		return "Ascon128"
    45  	case Ascon128a:
    46  		return "Ascon128a"
    47  	case Ascon80pq:
    48  		return "Ascon80pq"
    49  	default:
    50  		panic(ErrMode)
    51  	}
    52  }
    53  
    54  const (
    55  	Ascon128  Mode = 1
    56  	Ascon128a Mode = 2
    57  	Ascon80pq Mode = -1
    58  )
    59  
    60  const permA = 12
    61  
    62  type Cipher struct {
    63  	key  [3]uint64
    64  	mode Mode
    65  }
    66  
    67  // New returns a Cipher struct implementing the crypto/cipher.AEAD interface.
    68  // The key must be Mode.KeySize() bytes long, and the mode is one of Ascon128,
    69  // Ascon128a or Ascon80pq.
    70  func New(key []byte, m Mode) (*Cipher, error) {
    71  	if (m == Ascon128 || m == Ascon128a) && len(key) != KeySize {
    72  		return nil, ErrKeySize
    73  	}
    74  	if m == Ascon80pq && len(key) != KeySize80pq {
    75  		return nil, ErrKeySize
    76  	}
    77  	if !(m == Ascon128 || m == Ascon128a || m == Ascon80pq) {
    78  		return nil, ErrMode
    79  	}
    80  	c := new(Cipher)
    81  	c.mode = m
    82  	if m == Ascon80pq {
    83  		c.key[0] = uint64(binary.BigEndian.Uint32(key[0:4]))
    84  		c.key[1] = binary.BigEndian.Uint64(key[4:12])
    85  		c.key[2] = binary.BigEndian.Uint64(key[12:20])
    86  	} else {
    87  		c.key[0] = 0
    88  		c.key[1] = binary.BigEndian.Uint64(key[0:8])
    89  		c.key[2] = binary.BigEndian.Uint64(key[8:16])
    90  	}
    91  
    92  	return c, nil
    93  }
    94  
    95  // NonceSize returns the size of the nonce that must be passed to Seal
    96  // and Open.
    97  func (a *Cipher) NonceSize() int { return NonceSize }
    98  
    99  // Overhead returns the maximum difference between the lengths of a
   100  // plaintext and its ciphertext.
   101  func (a *Cipher) Overhead() int { return TagSize }
   102  
   103  // Seal encrypts and authenticates plaintext, authenticates the
   104  // additional data and appends the result to dst, returning the updated
   105  // slice. The nonce must be NonceSize() bytes long and unique for all
   106  // time, for a given key.
   107  //
   108  // To reuse plaintext's storage for the encrypted output, use plaintext[:0]
   109  // as dst. Otherwise, the remaining capacity of dst must not overlap plaintext.
   110  func (a *Cipher) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
   111  	if len(nonce) != NonceSize {
   112  		panic(ErrNonceSize)
   113  	}
   114  
   115  	ptLen := len(plaintext)
   116  	ret, out := sliceForAppend(dst, ptLen+TagSize)
   117  	ciphertext, tag := out[:ptLen], out[ptLen:]
   118  
   119  	var s [5]uint64
   120  	a.initialize(nonce, &s)
   121  	a.assocData(additionalData, &s)
   122  	a.procText(plaintext, ciphertext, true, &s)
   123  	a.finalize(tag, &s)
   124  
   125  	return ret
   126  }
   127  
   128  // Open decrypts and authenticates ciphertext, authenticates the
   129  // additional data and, if successful, appends the resulting plaintext
   130  // to dst, returning the updated slice. The nonce must be NonceSize()
   131  // bytes long and both it and the additional data must match the
   132  // value passed to Seal.
   133  //
   134  // To reuse ciphertext's storage for the decrypted output, use ciphertext[:0]
   135  // as dst. Otherwise, the remaining capacity of dst must not overlap plaintext.
   136  //
   137  // Even if the function fails, the contents of dst, up to its capacity,
   138  // may be overwritten.
   139  func (a *Cipher) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
   140  	if len(nonce) != NonceSize {
   141  		panic(ErrNonceSize)
   142  	}
   143  	if len(ciphertext) < TagSize {
   144  		return nil, ErrDecryption
   145  	}
   146  
   147  	ptLen := len(ciphertext) - TagSize
   148  	ret, out := sliceForAppend(dst, ptLen)
   149  	plaintext := out[:ptLen]
   150  	ciphertext, tag0 := ciphertext[:ptLen], ciphertext[ptLen:]
   151  	tag1 := (&[TagSize]byte{})[:]
   152  
   153  	var s [5]uint64
   154  	a.initialize(nonce, &s)
   155  	a.assocData(additionalData, &s)
   156  	a.procText(ciphertext, plaintext, false, &s)
   157  	a.finalize(tag1, &s)
   158  
   159  	if subtle.ConstantTimeCompare(tag0, tag1) == 0 {
   160  		return nil, ErrDecryption
   161  	}
   162  
   163  	return ret, nil
   164  }
   165  
   166  func abs(x int) int { m := uint(x >> (bits.UintSize - 1)); return int((uint(x) + m) ^ m) }
   167  
   168  // blockSize = 8 for Ascon128 and Ascon80pq, or 16 for Ascon128a.
   169  func (a *Cipher) blockSize() int { return abs(int(a.mode)) << 3 }
   170  
   171  // permB = 6 for Ascon128 and Ascon80pq, or 8 for Ascon128a.
   172  func (a *Cipher) permB() int { return (abs(int(a.mode)) + 2) << 1 }
   173  
   174  func (a *Cipher) initialize(nonce []byte, s *[5]uint64) {
   175  	bcs := uint64(a.blockSize())
   176  	pB := uint64(a.permB())
   177  	kS := uint64(a.mode.KeySize())
   178  
   179  	s[0] = ((kS * 8) << 56) | ((bcs * 8) << 48) | (permA << 40) | (pB << 32) | a.key[0]
   180  	s[1] = a.key[1]
   181  	s[2] = a.key[2]
   182  	s[3] = binary.BigEndian.Uint64(nonce[0:8])
   183  	s[4] = binary.BigEndian.Uint64(nonce[8:16])
   184  
   185  	perm(permA, s)
   186  
   187  	s[2] ^= a.key[0]
   188  	s[3] ^= a.key[1]
   189  	s[4] ^= a.key[2]
   190  }
   191  
   192  func (a *Cipher) assocData(add []byte, s *[5]uint64) {
   193  	bcs := a.blockSize()
   194  	pB := a.permB()
   195  	if len(add) > 0 {
   196  		for ; len(add) >= bcs; add = add[bcs:] {
   197  			for i := 0; i < bcs; i += 8 {
   198  				s[i/8] ^= binary.BigEndian.Uint64(add[i : i+8])
   199  			}
   200  			perm(pB, s)
   201  		}
   202  		for i := 0; i < len(add); i++ {
   203  			s[i/8] ^= uint64(add[i]) << (56 - 8*(i%8))
   204  		}
   205  		s[len(add)/8] ^= uint64(0x80) << (56 - 8*(len(add)%8))
   206  		perm(pB, s)
   207  	}
   208  	s[4] ^= 0x01
   209  }
   210  
   211  func (a *Cipher) procText(in, out []byte, enc bool, s *[5]uint64) {
   212  	bcs := a.blockSize()
   213  	pB := a.permB()
   214  	mask := uint64(0)
   215  	if enc {
   216  		mask -= 1
   217  	}
   218  
   219  	for ; len(in) >= bcs; in, out = in[bcs:], out[bcs:] {
   220  		for i := 0; i < bcs; i += 8 {
   221  			inW := binary.BigEndian.Uint64(in[i : i+8])
   222  			outW := s[i/8] ^ inW
   223  			binary.BigEndian.PutUint64(out[i:i+8], outW)
   224  
   225  			s[i/8] = (inW &^ mask) | (outW & mask)
   226  		}
   227  		perm(pB, s)
   228  	}
   229  
   230  	mask8 := byte(mask & 0xFF)
   231  	for i := 0; i < len(in); i++ {
   232  		off := 56 - (8 * (i % 8))
   233  		si := byte((s[i/8] >> off) & 0xFF)
   234  		inB := in[i]
   235  		outB := si ^ inB
   236  		out[i] = outB
   237  		ss := inB&^mask8 | outB&mask8
   238  		s[i/8] = (s[i/8] &^ (0xFF << off)) | uint64(ss)<<off
   239  	}
   240  	s[len(in)/8] ^= uint64(0x80) << (56 - 8*(len(in)%8))
   241  }
   242  
   243  func (a *Cipher) finalize(tag []byte, s *[5]uint64) {
   244  	bcs := a.blockSize()
   245  	if a.mode == Ascon80pq {
   246  		s[bcs/8+0] ^= a.key[0]<<32 | a.key[1]>>32
   247  		s[bcs/8+1] ^= a.key[1]<<32 | a.key[2]>>32
   248  		s[bcs/8+2] ^= a.key[2] << 32
   249  	} else {
   250  		s[bcs/8+0] ^= a.key[1]
   251  		s[bcs/8+1] ^= a.key[2]
   252  	}
   253  
   254  	perm(permA, s)
   255  	binary.BigEndian.PutUint64(tag[0:8], s[3]^a.key[1])
   256  	binary.BigEndian.PutUint64(tag[8:16], s[4]^a.key[2])
   257  }
   258  
   259  func perm(n int, s *[5]uint64) {
   260  	x0, x1, x2, x3, x4 := s[0], s[1], s[2], s[3], s[4]
   261  	for i := permA - n; i < permA; i++ {
   262  		// pC -- addition of constants
   263  		x2 ^= uint64((0xF-i)<<4 | i)
   264  
   265  		// pS -- substitution layer
   266  		// Figure 6 from Spec [DHVV18,Dae18]
   267  		// https://ascon.iaik.tugraz.at/files/asconv12-nist.pdf
   268  		x0 ^= x4
   269  		x4 ^= x3
   270  		x2 ^= x1
   271  		t0 := x0 & (^x4)
   272  		t1 := x2 & (^x1)
   273  		x0 ^= t1
   274  		t1 = x4 & (^x3)
   275  		x2 ^= t1
   276  		t1 = x1 & (^x0)
   277  		x4 ^= t1
   278  		t1 = x3 & (^x2)
   279  		x1 ^= t1
   280  		x3 ^= t0
   281  		x1 ^= x0
   282  		x3 ^= x2
   283  		x0 ^= x4
   284  		x2 = ^x2
   285  
   286  		// pL -- linear diffusion layer
   287  		x0 ^= bits.RotateLeft64(x0, -19) ^ bits.RotateLeft64(x0, -28)
   288  		x1 ^= bits.RotateLeft64(x1, -61) ^ bits.RotateLeft64(x1, -39)
   289  		x2 ^= bits.RotateLeft64(x2, -1) ^ bits.RotateLeft64(x2, -6)
   290  		x3 ^= bits.RotateLeft64(x3, -10) ^ bits.RotateLeft64(x3, -17)
   291  		x4 ^= bits.RotateLeft64(x4, -7) ^ bits.RotateLeft64(x4, -41)
   292  	}
   293  	s[0], s[1], s[2], s[3], s[4] = x0, x1, x2, x3, x4
   294  }
   295  
   296  // sliceForAppend takes a slice and a requested number of bytes. It returns a
   297  // slice with the contents of the given slice followed by that many bytes and a
   298  // second slice that aliases into it and contains only the extra bytes. If the
   299  // original slice has sufficient capacity then no allocation is performed.
   300  func sliceForAppend(in []byte, n int) (head, tail []byte) {
   301  	if total := len(in) + n; cap(in) >= total {
   302  		head = in[:total]
   303  	} else {
   304  		head = make([]byte, total)
   305  		copy(head, in)
   306  	}
   307  	tail = head[len(in):]
   308  	return
   309  }
   310  
   311  var (
   312  	ErrKeySize    = errors.New("ascon: bad key size")
   313  	ErrNonceSize  = errors.New("ascon: bad nonce size")
   314  	ErrDecryption = errors.New("ascon: invalid ciphertext")
   315  	ErrMode       = errors.New("ascon: invalid cipher mode")
   316  )