]> Cypherpunks.ru repositories - gogost.git/blob - src/cypherpunks.ru/gogost/mgm/mode.go
Remove unnecessary condition
[gogost.git] / src / cypherpunks.ru / gogost / mgm / mode.go
1 // GoGOST -- Pure Go GOST cryptographic functions library
2 // Copyright (C) 2015-2019 Sergey Matveev <stargrave@stargrave.org>
3 //
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.
8 //
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.
13 //
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/>.
16
17 // Multilinear Galois Mode (MGM) block cipher mode.
18 package mgm
19
20 import (
21         "crypto/cipher"
22         "crypto/hmac"
23         "encoding/binary"
24         "errors"
25         "math/big"
26 )
27
28 var (
29         R64  *big.Int = big.NewInt(0)
30         R128 *big.Int = big.NewInt(0)
31 )
32
33 func init() {
34         R64.SetBytes([]byte{
35                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b,
36         })
37         R128.SetBytes([]byte{
38                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
40         })
41 }
42
43 type MGM struct {
44         maxSize   uint64
45         cipher    cipher.Block
46         blockSize int
47         tagSize   int
48         icn       []byte
49         bufP      []byte
50         bufC      []byte
51         padded    []byte
52         sum       []byte
53
54         x      *big.Int
55         y      *big.Int
56         z      *big.Int
57         maxBit int
58         r      *big.Int
59         mulBuf []byte
60 }
61
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")
66         }
67         if tagSize < 4 || tagSize > blockSize {
68                 return nil, errors.New("invalid tag size")
69         }
70         mgm := MGM{
71                 maxSize:   uint64(1<<uint(blockSize*8/2) - 1),
72                 cipher:    cipher,
73                 blockSize: blockSize,
74                 tagSize:   tagSize,
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),
80                 x:         big.NewInt(0),
81                 y:         big.NewInt(0),
82                 z:         big.NewInt(0),
83                 mulBuf:    make([]byte, blockSize),
84         }
85         if blockSize == 8 {
86                 mgm.maxBit = 64 - 1
87                 mgm.r = R64
88         } else {
89                 mgm.maxBit = 128 - 1
90                 mgm.r = R128
91         }
92         return &mgm, nil
93 }
94
95 func (mgm *MGM) NonceSize() int {
96         return mgm.blockSize
97 }
98
99 func (mgm *MGM) Overhead() int {
100         return mgm.tagSize
101 }
102
103 func incr(data []byte) {
104         if len(data) == 4 {
105                 binary.BigEndian.PutUint32(data, binary.BigEndian.Uint32(data)+1)
106         } else {
107                 binary.BigEndian.PutUint64(data, binary.BigEndian.Uint64(data)+1)
108         }
109 }
110
111 func xor(dst, src1, src2 []byte) {
112         for i := 0; i < len(src1); i++ {
113                 dst[i] = src1[i] ^ src2[i]
114         }
115 }
116
117 func (mgm *MGM) validateNonce(nonce []byte) {
118         if len(nonce) != mgm.blockSize {
119                 panic("nonce length must be equal to cipher's blocksize")
120         }
121         if nonce[0]&0x80 > 0 {
122                 panic("nonce must not have higher bit set")
123         }
124 }
125
126 func (mgm *MGM) validateSizes(text, additionalData []byte) {
127         if len(text) == 0 && len(additionalData) == 0 {
128                 panic("at least either *text or additionalData must be provided")
129         }
130         if uint64(len(additionalData)) > mgm.maxSize {
131                 panic("additionalData is too big")
132         }
133         if uint64(len(text)+len(additionalData)) > mgm.maxSize {
134                 panic("*text with additionalData are too big")
135         }
136 }
137
138 func (mgm *MGM) auth(out, text, ad []byte) {
139         for i := 0; i < mgm.blockSize; i++ {
140                 mgm.sum[i] = 0
141         }
142         adLen := len(ad) * 8
143         textLen := len(text) * 8
144         mgm.icn[0] |= 0x80
145         mgm.cipher.Encrypt(mgm.bufP, mgm.icn) // Z_1 = E_K(1 || ICN)
146         for len(ad) >= mgm.blockSize {
147                 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP) // H_i = E_K(Z_i)
148                 xor(                                   // sum (xor)= H_i (x) A_i
149                         mgm.sum,
150                         mgm.sum,
151                         mgm.mul(mgm.bufC, ad[:mgm.blockSize]),
152                 )
153                 incr(mgm.bufP[:mgm.blockSize/2]) // Z_{i+1} = incr_l(Z_i)
154                 ad = ad[mgm.blockSize:]
155         }
156         if len(ad) > 0 {
157                 copy(mgm.padded, ad)
158                 for i := len(ad); i < mgm.blockSize; i++ {
159                         mgm.padded[i] = 0
160                 }
161                 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP)
162                 xor(mgm.sum, mgm.sum, mgm.mul(mgm.bufC, mgm.padded))
163                 incr(mgm.bufP[:mgm.blockSize/2])
164         }
165
166         for len(text) >= mgm.blockSize {
167                 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP) // H_{h+j} = E_K(Z_{h+j})
168                 xor(                                   // sum (xor)= H_{h+j} (x) C_j
169                         mgm.sum,
170                         mgm.sum,
171                         mgm.mul(mgm.bufC, text[:mgm.blockSize]),
172                 )
173                 incr(mgm.bufP[:mgm.blockSize/2]) // Z_{h+j+1} = incr_l(Z_{h+j})
174                 text = text[mgm.blockSize:]
175         }
176         if len(text) > 0 {
177                 copy(mgm.padded, text)
178                 for i := len(text); i < mgm.blockSize; i++ {
179                         mgm.padded[i] = 0
180                 }
181                 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP)
182                 xor(mgm.sum, mgm.sum, mgm.mul(mgm.bufC, mgm.padded))
183                 incr(mgm.bufP[:mgm.blockSize/2])
184         }
185
186         mgm.cipher.Encrypt(mgm.bufP, mgm.bufP) // H_{h+q+1} = E_K(Z_{h+q+1})
187         // len(A) || len(C)
188         if mgm.blockSize == 8 {
189                 binary.BigEndian.PutUint32(mgm.bufC, uint32(adLen))
190                 binary.BigEndian.PutUint32(mgm.bufC[mgm.blockSize/2:], uint32(textLen))
191         } else {
192                 binary.BigEndian.PutUint64(mgm.bufC, uint64(adLen))
193                 binary.BigEndian.PutUint64(mgm.bufC[mgm.blockSize/2:], uint64(textLen))
194         }
195         // sum (xor)= H_{h+q+1} (x) (len(A) || len(C))
196         xor(mgm.sum, mgm.sum, mgm.mul(mgm.bufP, mgm.bufC))
197         mgm.cipher.Encrypt(mgm.bufP, mgm.sum) // E_K(sum)
198         copy(out, mgm.bufP[:mgm.tagSize])     // MSB_S(E_K(sum))
199 }
200
201 func (mgm *MGM) crypt(out, in []byte) {
202         mgm.icn[0] &= 0x7F
203         mgm.cipher.Encrypt(mgm.bufP, mgm.icn) // Y_1 = E_K(0 || ICN)
204         for len(in) >= mgm.blockSize {
205                 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP) // E_K(Y_i)
206                 xor(out, mgm.bufC, in)                 // C_i = P_i (xor) E_K(Y_i)
207                 incr(mgm.bufP[mgm.blockSize/2:])       // Y_i = incr_r(Y_{i-1})
208                 out = out[mgm.blockSize:]
209                 in = in[mgm.blockSize:]
210         }
211         if len(in) > 0 {
212                 mgm.cipher.Encrypt(mgm.bufC, mgm.bufP)
213                 xor(out, in, mgm.bufC)
214         }
215 }
216
217 func (mgm *MGM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
218         mgm.validateNonce(nonce)
219         mgm.validateSizes(plaintext, additionalData)
220         if uint64(len(plaintext)) > mgm.maxSize {
221                 panic("plaintext is too big")
222         }
223         ret, out := sliceForAppend(dst, len(plaintext)+mgm.tagSize)
224         copy(mgm.icn, nonce)
225         mgm.crypt(out, plaintext)
226         mgm.auth(
227                 out[len(plaintext):len(plaintext)+mgm.tagSize],
228                 out[:len(plaintext)],
229                 additionalData,
230         )
231         return ret
232 }
233
234 func (mgm *MGM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
235         mgm.validateNonce(nonce)
236         mgm.validateSizes(ciphertext, additionalData)
237         if uint64(len(ciphertext)-mgm.tagSize) > mgm.maxSize {
238                 panic("ciphertext is too big")
239         }
240         ret, out := sliceForAppend(dst, len(ciphertext)-mgm.tagSize)
241         ct := ciphertext[:len(ciphertext)-mgm.tagSize]
242         copy(mgm.icn, nonce)
243         mgm.auth(mgm.sum, ct, additionalData)
244         if !hmac.Equal(mgm.sum[:mgm.tagSize], ciphertext[len(ciphertext)-mgm.tagSize:]) {
245                 return nil, errors.New("invalid authentication tag")
246         }
247         mgm.crypt(out, ct)
248         return ret, nil
249 }