github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/stdlibs/crypto/chacha20/chacha/chacha.gno (about) 1 // Copyright (c) 2016 Andreas Auernhammer. All rights reserved. 2 // Use of this source code is governed by a license that can be 3 // found in the LICENSE file. 4 5 // Package chacha implements some low-level functions of the 6 // ChaCha cipher family. 7 package chacha // import "github.com/aead/chacha20/chacha" 8 9 import ( 10 "encoding/binary" 11 "errors" 12 "math" 13 ) 14 15 const ( 16 // NonceSize is the size of the ChaCha20 nonce in bytes. 17 NonceSize = 8 18 19 // INonceSize is the size of the IETF-ChaCha20 nonce in bytes. 20 INonceSize = 12 21 22 // XNonceSize is the size of the XChaCha20 nonce in bytes. 23 XNonceSize = 24 24 25 // KeySize is the size of the key in bytes. 26 KeySize = 32 27 ) 28 29 var ( 30 useSSE2 bool 31 useSSSE3 bool 32 useAVX bool 33 useAVX2 bool 34 ) 35 36 var ( 37 errKeySize = errors.New("chacha20/chacha: bad key length") 38 errInvalidNonce = errors.New("chacha20/chacha: bad nonce length") 39 ) 40 41 func setup(state *[64]byte, nonce, key []byte) (err error) { 42 if len(key) != KeySize { 43 err = errKeySize 44 return 45 } 46 var Nonce [16]byte 47 switch len(nonce) { 48 case NonceSize: 49 copy(Nonce[8:], nonce) 50 initialize(state, key, &Nonce) 51 case INonceSize: 52 copy(Nonce[4:], nonce) 53 initialize(state, key, &Nonce) 54 case XNonceSize: 55 var tmpKey [32]byte 56 var hNonce [16]byte 57 58 copy(hNonce[:], nonce[:16]) 59 copy(tmpKey[:], key) 60 HChaCha20(&tmpKey, &hNonce, &tmpKey) 61 copy(Nonce[8:], nonce[16:]) 62 initialize(state, tmpKey[:], &Nonce) 63 64 // BUG(aead): A "good" compiler will remove this (optimizations) 65 // But using the provided key instead of tmpKey, 66 // will change the key (-> probably confuses users) 67 for i := range tmpKey { 68 tmpKey[i] = 0 69 } 70 default: 71 err = errInvalidNonce 72 } 73 return 74 } 75 76 // XORKeyStream crypts bytes from src to dst using the given nonce and key. 77 // The length of the nonce determinds the version of ChaCha20: 78 // - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period. 79 // - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period. 80 // - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period. 81 // The rounds argument specifies the number of rounds performed for keystream 82 // generation - valid values are 8, 12 or 20. The src and dst may be the same slice 83 // but otherwise should not overlap. If len(dst) < len(src) this function panics. 84 // If the nonce is neither 64, 96 nor 192 bits long, this function panics. 85 func XORKeyStream(dst, src, nonce, key []byte, rounds int) { 86 if rounds != 20 && rounds != 12 && rounds != 8 { 87 panic("chacha20/chacha: bad number of rounds") 88 } 89 if len(dst) < len(src) { 90 panic("chacha20/chacha: dst buffer is to small") 91 } 92 if len(nonce) == INonceSize && uint64(len(src)) > (1<<38) { 93 panic("chacha20/chacha: src is too large") 94 } 95 96 var block, state [64]byte 97 if err := setup(&state, nonce, key); err != nil { 98 panic(err) 99 } 100 xorKeyStream(dst, src, &block, &state, rounds) 101 } 102 103 // Cipher implements ChaCha20/r (XChaCha20/r) for a given number of rounds r. 104 type Cipher struct { 105 state, block [64]byte 106 off int 107 rounds int // 20 for ChaCha20 108 noncesize int 109 } 110 111 // NewCipher returns a new *chacha.Cipher implementing the ChaCha20/r or XChaCha20/r 112 // (r = 8, 12 or 20) stream cipher. The nonce must be unique for one key for all time. 113 // The length of the nonce determinds the version of ChaCha20: 114 // - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period. 115 // - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period. 116 // - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period. 117 // If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned. 118 func NewCipher(nonce, key []byte, rounds int) (*Cipher, error) { 119 if rounds != 20 && rounds != 12 && rounds != 8 { 120 panic("chacha20/chacha: bad number of rounds") 121 } 122 123 c := new(Cipher) 124 if err := setup(&(c.state), nonce, key); err != nil { 125 return nil, err 126 } 127 c.rounds = rounds 128 129 if len(nonce) == INonceSize { 130 c.noncesize = INonceSize 131 } else { 132 c.noncesize = NonceSize 133 } 134 135 return c, nil 136 } 137 138 // XORKeyStream crypts bytes from src to dst. Src and dst may be the same slice 139 // but otherwise should not overlap. If len(dst) < len(src) the function panics. 140 func (c *Cipher) XORKeyStream(dst, src []byte) { 141 if len(dst) < len(src) { 142 panic("chacha20/chacha: dst buffer is to small") 143 } 144 145 if c.off > 0 { 146 n := len(c.block[c.off:]) 147 if len(src) <= n { 148 for i, v := range src { 149 dst[i] = v ^ c.block[c.off] 150 c.off++ 151 } 152 if c.off == 64 { 153 c.off = 0 154 } 155 return 156 } 157 158 for i, v := range c.block[c.off:] { 159 dst[i] = src[i] ^ v 160 } 161 src = src[n:] 162 dst = dst[n:] 163 c.off = 0 164 } 165 166 // check for counter overflow 167 blocksToXOR := len(src) / 64 168 if len(src)%64 != 0 { 169 blocksToXOR++ 170 } 171 var overflow bool 172 if c.noncesize == INonceSize { 173 overflow = binary.LittleEndian.Uint32(c.state[48:]) > math.MaxUint32-uint32(blocksToXOR) 174 } else { 175 overflow = binary.LittleEndian.Uint64(c.state[48:]) > math.MaxUint64-uint64(blocksToXOR) 176 } 177 if overflow { 178 panic("chacha20/chacha: counter overflow") 179 } 180 181 c.off += xorKeyStream(dst, src, &(c.block), &(c.state), c.rounds) 182 } 183 184 // SetCounter skips ctr * 64 byte blocks. SetCounter(0) resets the cipher. 185 // This function always skips the unused keystream of the current 64 byte block. 186 func (c *Cipher) SetCounter(ctr uint64) { 187 if c.noncesize == INonceSize { 188 binary.LittleEndian.PutUint32(c.state[48:], uint32(ctr)) 189 } else { 190 binary.LittleEndian.PutUint64(c.state[48:], ctr) 191 } 192 c.off = 0 193 } 194 195 // HChaCha20 generates 32 pseudo-random bytes from a 128 bit nonce and a 256 bit secret key. 196 // It can be used as a key-derivation-function (KDF). 197 func HChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { hChaCha20(out, nonce, key) }