1 // GoGOST -- Pure Go GOST cryptographic functions library
2 // Copyright (C) 2015-2019 Sergey Matveev <stargrave@stargrave.org>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // Multilinear Galois Mode (MGM) block cipher mode.
29 R64 *big.Int = big.NewInt(0)
30 R128 *big.Int = big.NewInt(0)
35 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b,
38 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
62 func NewMGM(cipher cipher.Block, tagSize int) (cipher.AEAD, error) {
63 blockSize := cipher.BlockSize()
64 if !(blockSize == 8 || blockSize == 16) {
65 return nil, errors.New("MGM supports only 64/128 blocksizes")
67 if tagSize < 4 || tagSize > blockSize {
68 return nil, errors.New("invalid tag size")
71 maxSize: uint64(1<<uint(blockSize*8/2) - 1),
75 icn: make([]byte, blockSize),
76 bufP: make([]byte, blockSize),
77 bufC: make([]byte, blockSize),
78 padded: make([]byte, blockSize),
79 sum: make([]byte, blockSize),
83 mulBuf: make([]byte, blockSize),
95 func (mgm *MGM) NonceSize() int {
99 func (mgm *MGM) Overhead() int {
103 func incr(data []byte) {
104 for i := len(data) - 1; i >= 0; i-- {
112 func xor(dst, src1, src2 []byte) {
113 for i := 0; i < len(src1); i++ {
114 dst[i] = src1[i] ^ src2[i]
118 func (mgm *MGM) validateNonce(nonce []byte) {
119 if len(nonce) != mgm.blockSize {
120 panic("nonce length must be equal to cipher's blocksize")
122 if nonce[0]&0x80 > 0 {
123 panic("nonce must not have higher bit set")
127 func (mgm *MGM) validateSizes(text, additionalData []byte) {
128 if len(text) == 0 && len(additionalData) == 0 {
129 panic("at least either *text or additionalData must be provided")
131 if uint64(len(additionalData)) > mgm.maxSize {
132 panic("additionalData is too big")
134 if uint64(len(text)+len(additionalData)) > mgm.maxSize {
135 panic("*text with additionalData are too big")
139 func (mgm *MGM) auth(out, text, ad []byte) {
140 for i := 0; i < mgm.blockSize; i++ {
144 textLen := len(text) * 8
146 mgm.cipher.Encrypt(mgm.bufP, mgm.icn) // Z_1 = E_K(1 || ICN)
147 for len(ad) >= mgm.blockSize {
148 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP) // H_i = E_K(Z_i)
149 xor( // sum (xor)= H_i (x) A_i
152 mgm.mul(mgm.bufC, ad[:mgm.blockSize]),
154 incr(mgm.bufP[:mgm.blockSize/2]) // Z_{i+1} = incr_l(Z_i)
155 ad = ad[mgm.blockSize:]
159 for i := len(ad); i < mgm.blockSize; i++ {
162 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP)
163 xor(mgm.sum, mgm.sum, mgm.mul(mgm.bufC, mgm.padded))
164 incr(mgm.bufP[:mgm.blockSize/2])
167 for len(text) >= mgm.blockSize {
168 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP) // H_{h+j} = E_K(Z_{h+j})
169 xor( // sum (xor)= H_{h+j} (x) C_j
172 mgm.mul(mgm.bufC, text[:mgm.blockSize]),
174 incr(mgm.bufP[:mgm.blockSize/2]) // Z_{h+j+1} = incr_l(Z_{h+j})
175 text = text[mgm.blockSize:]
178 copy(mgm.padded, text)
179 for i := len(text); i < mgm.blockSize; i++ {
182 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP)
183 xor(mgm.sum, mgm.sum, mgm.mul(mgm.bufC, mgm.padded))
184 incr(mgm.bufP[:mgm.blockSize/2])
187 mgm.cipher.Encrypt(mgm.bufP, mgm.bufP) // H_{h+q+1} = E_K(Z_{h+q+1})
189 if mgm.blockSize == 8 {
190 binary.BigEndian.PutUint32(mgm.bufC, uint32(adLen))
191 binary.BigEndian.PutUint32(mgm.bufC[mgm.blockSize/2:], uint32(textLen))
193 binary.BigEndian.PutUint64(mgm.bufC, uint64(adLen))
194 binary.BigEndian.PutUint64(mgm.bufC[mgm.blockSize/2:], uint64(textLen))
196 // sum (xor)= H_{h+q+1} (x) (len(A) || len(C))
197 xor(mgm.sum, mgm.sum, mgm.mul(mgm.bufP, mgm.bufC))
198 mgm.cipher.Encrypt(mgm.bufP, mgm.sum) // E_K(sum)
199 copy(out, mgm.bufP[:mgm.tagSize]) // MSB_S(E_K(sum))
202 func (mgm *MGM) crypt(out, in []byte) {
204 mgm.cipher.Encrypt(mgm.bufP, mgm.icn) // Y_1 = E_K(0 || ICN)
205 for len(in) >= mgm.blockSize {
206 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP) // E_K(Y_i)
207 xor(out, mgm.bufC, in) // C_i = P_i (xor) E_K(Y_i)
208 incr(mgm.bufP[mgm.blockSize/2:]) // Y_i = incr_r(Y_{i-1})
209 out = out[mgm.blockSize:]
210 in = in[mgm.blockSize:]
213 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP)
214 xor(out, in, mgm.bufC)
218 func (mgm *MGM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
219 mgm.validateNonce(nonce)
220 mgm.validateSizes(plaintext, additionalData)
221 if uint64(len(plaintext)) > mgm.maxSize {
222 panic("plaintext is too big")
224 ret, out := sliceForAppend(dst, len(plaintext)+mgm.tagSize)
226 mgm.crypt(out, plaintext)
228 out[len(plaintext):len(plaintext)+mgm.tagSize],
229 out[:len(plaintext)],
235 func (mgm *MGM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
236 mgm.validateNonce(nonce)
237 mgm.validateSizes(ciphertext, additionalData)
238 if uint64(len(ciphertext)-mgm.tagSize) > mgm.maxSize {
239 panic("ciphertext is too big")
241 ret, out := sliceForAppend(dst, len(ciphertext)-mgm.tagSize)
242 ct := ciphertext[:len(ciphertext)-mgm.tagSize]
244 mgm.auth(mgm.sum, ct, additionalData)
245 if !hmac.Equal(mgm.sum[:mgm.tagSize], ciphertext[len(ciphertext)-mgm.tagSize:]) {
246 return nil, errors.New("invalid authentication tag")