// GoGOST -- Pure Go GOST cryptographic functions library
-// Copyright (C) 2015-2021 Sergey Matveev <stargrave@stargrave.org>
+// Copyright (C) 2015-2023 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
"crypto/hmac"
"encoding/binary"
"errors"
- "math/big"
)
-var (
- R64 *big.Int = big.NewInt(0)
- R128 *big.Int = big.NewInt(0)
-)
+var InvalidTag = errors.New("gogost/mgm: invalid authentication tag")
-func init() {
- R64.SetBytes([]byte{
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b,
- })
- R128.SetBytes([]byte{
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
- })
+type Mul interface {
+ Mul(x, y []byte) []byte
}
type MGM struct {
bufC []byte
padded []byte
sum []byte
-
- x *big.Int
- y *big.Int
- z *big.Int
- maxBit int
- r *big.Int
- mulBuf []byte
+ mul Mul
}
func NewMGM(cipher cipher.Block, tagSize int) (cipher.AEAD, error) {
bufC: make([]byte, blockSize),
padded: make([]byte, blockSize),
sum: make([]byte, blockSize),
- x: big.NewInt(0),
- y: big.NewInt(0),
- z: big.NewInt(0),
- mulBuf: make([]byte, blockSize),
}
if blockSize == 8 {
- mgm.maxBit = 64 - 1
- mgm.r = R64
+ mgm.mul = newMul64()
} else {
- mgm.maxBit = 128 - 1
- mgm.r = R128
+ mgm.mul = newMul128()
}
return &mgm, nil
}
xor( // sum (xor)= H_i (x) A_i
mgm.sum,
mgm.sum,
- mgm.mul(mgm.bufC, ad[:mgm.BlockSize]),
+ mgm.mul.Mul(mgm.bufC, ad[:mgm.BlockSize]),
)
incr(mgm.bufP[:mgm.BlockSize/2]) // Z_{i+1} = incr_l(Z_i)
ad = ad[mgm.BlockSize:]
mgm.padded[i] = 0
}
mgm.cipher.Encrypt(mgm.bufC, mgm.bufP)
- xor(mgm.sum, mgm.sum, mgm.mul(mgm.bufC, mgm.padded))
+ xor(mgm.sum, mgm.sum, mgm.mul.Mul(mgm.bufC, mgm.padded))
incr(mgm.bufP[:mgm.BlockSize/2])
}
xor( // sum (xor)= H_{h+j} (x) C_j
mgm.sum,
mgm.sum,
- mgm.mul(mgm.bufC, text[:mgm.BlockSize]),
+ mgm.mul.Mul(mgm.bufC, text[:mgm.BlockSize]),
)
incr(mgm.bufP[:mgm.BlockSize/2]) // Z_{h+j+1} = incr_l(Z_{h+j})
text = text[mgm.BlockSize:]
mgm.padded[i] = 0
}
mgm.cipher.Encrypt(mgm.bufC, mgm.bufP)
- xor(mgm.sum, mgm.sum, mgm.mul(mgm.bufC, mgm.padded))
+ xor(mgm.sum, mgm.sum, mgm.mul.Mul(mgm.bufC, mgm.padded))
incr(mgm.bufP[:mgm.BlockSize/2])
}
binary.BigEndian.PutUint64(mgm.bufC[mgm.BlockSize/2:], uint64(textLen))
}
// sum (xor)= H_{h+q+1} (x) (len(A) || len(C))
- xor(mgm.sum, mgm.sum, mgm.mul(mgm.bufP, mgm.bufC))
+ xor(mgm.sum, mgm.sum, mgm.mul.Mul(mgm.bufC, mgm.bufP))
mgm.cipher.Encrypt(mgm.bufP, mgm.sum) // E_K(sum)
copy(out, mgm.bufP[:mgm.TagSize]) // MSB_S(E_K(sum))
}
return ret
}
+// Open the authenticated ciphertext. If authentication tag is invalid,
+// then InvalidTag error is returned.
func (mgm *MGM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
mgm.validateNonce(nonce)
mgm.validateSizes(ciphertext, additionalData)
- if uint64(len(ciphertext)-mgm.tagSize) > mgm.maxSize {
+ if len(ciphertext) < mgm.TagSize {
+ return nil, errors.New("ciphertext is too short")
+ }
if uint64(len(ciphertext)-mgm.TagSize) > mgm.MaxSize {
panic("ciphertext is too big")
}
copy(mgm.icn, nonce)
mgm.auth(mgm.sum, ct, additionalData)
if !hmac.Equal(mgm.sum[:mgm.TagSize], ciphertext[len(ciphertext)-mgm.TagSize:]) {
- return nil, errors.New("gogost/mgm: invalid authentication tag")
+ return nil, InvalidTag
}
mgm.crypt(out, ct)
return ret, nil