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 )