mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-23 02:36:20 +08:00
213 lines
6.2 KiB
Go
213 lines
6.2 KiB
Go
![]() |
package cipher
|
||
|
|
||
|
import (
|
||
|
_cipher "crypto/cipher"
|
||
|
"encoding/binary"
|
||
|
"errors"
|
||
|
|
||
|
"github.com/emmansun/gmsm/internal/alias"
|
||
|
"github.com/emmansun/gmsm/internal/subtle"
|
||
|
)
|
||
|
|
||
|
// A LengthPreservingMode represents a block cipher running in a length preserving mode (HCTR,
|
||
|
// HCTR2 etc).
|
||
|
type LengthPreservingMode interface {
|
||
|
// Encrypt encrypts a number of plaintext bytes. The length of
|
||
|
// src must be NOT smaller than block size. Dst and src must overlap
|
||
|
// entirely or not at all.
|
||
|
//
|
||
|
// If len(dst) < len(src), Encrypt should panic. It is acceptable
|
||
|
// to pass a dst bigger than src, and in that case, Encrypt will
|
||
|
// only update dst[:len(src)] and will not touch the rest of dst.
|
||
|
//
|
||
|
// Multiple calls to Encrypt behave NOT same as if the concatenation of
|
||
|
// the src buffers was passed in a single run.
|
||
|
Encrypt(dst, src []byte)
|
||
|
|
||
|
// Decrypt decrypts a number of ciphertext bytes. The length of
|
||
|
// src must be NOT smaller than block size. Dst and src must overlap
|
||
|
// entirely or not at all.
|
||
|
//
|
||
|
// If len(dst) < len(src), Decrypt should panic. It is acceptable
|
||
|
// to pass a dst bigger than src, and in that case, Decrypt will
|
||
|
// only update dst[:len(src)] and will not touch the rest of dst.
|
||
|
//
|
||
|
// Multiple calls to Decrypt behave NOT same as if the concatenation of
|
||
|
// the src buffers was passed in a single run.
|
||
|
Decrypt(dst, src []byte)
|
||
|
}
|
||
|
|
||
|
// hctr represents a Varaible-Input-Length enciphering mode with a specific block cipher,
|
||
|
// and specific tweak and a hash key. See
|
||
|
// https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288
|
||
|
// GB/T 17964-2021 第11章 带泛杂凑函数的计数器工作模式
|
||
|
type hctr struct {
|
||
|
cipher _cipher.Block
|
||
|
tweak [blockSize]byte
|
||
|
hkey [blockSize]byte
|
||
|
}
|
||
|
|
||
|
// NewHCTR returns a [LengthPreservingMode] which encrypts/decrypts useing the given [Block]
|
||
|
// in HCTR mode. The lenght of tweak and hash key must be the same as the [Block]'s block size.
|
||
|
func NewHCTR(cipher _cipher.Block, tweak, hkey []byte) (LengthPreservingMode, error) {
|
||
|
if len(tweak) != blockSize || len(hkey) != blockSize {
|
||
|
return nil, errors.New("hctr: invalid tweak and/or hash key length")
|
||
|
}
|
||
|
c := &hctr{}
|
||
|
c.cipher = cipher
|
||
|
copy(c.hkey[:], hkey)
|
||
|
copy(c.tweak[:], tweak)
|
||
|
return c, nil
|
||
|
}
|
||
|
|
||
|
func _mul2(v *[blockSize]byte) {
|
||
|
var carryIn byte
|
||
|
for j := range v {
|
||
|
carryOut := (v[j] << 7) & 0x80
|
||
|
v[j] = (v[j] >> 1) + carryIn
|
||
|
carryIn = carryOut
|
||
|
}
|
||
|
if carryIn != 0 {
|
||
|
v[0] ^= 0xE1 // 1<<7 | 1<<6 | 1<<5 | 1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// mul sets y to y*hkey.
|
||
|
func (h *hctr) mul(y *[blockSize]byte) {
|
||
|
var z [blockSize]byte
|
||
|
for _, i := range h.hkey {
|
||
|
for k := 0; k < 8; k++ {
|
||
|
if (i>>(7-k))&1 == 1 {
|
||
|
subtle.XORBytes(z[:], z[:], y[:])
|
||
|
}
|
||
|
_mul2(y)
|
||
|
}
|
||
|
}
|
||
|
copy(y[:], z[:])
|
||
|
}
|
||
|
|
||
|
// Universal Hash Function.
|
||
|
// Chapter 3.3 in https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288.
|
||
|
func (h *hctr) uhash(m []byte, dst *[blockSize]byte) {
|
||
|
for k := 0; k < blockSize; k++ {
|
||
|
dst[k] = 0
|
||
|
}
|
||
|
msg := m
|
||
|
for len(msg) >= blockSize {
|
||
|
subtle.XORBytes(dst[:], dst[:], msg[:blockSize])
|
||
|
h.mul(dst)
|
||
|
msg = msg[blockSize:]
|
||
|
}
|
||
|
var v [blockSize]byte
|
||
|
if len(msg) > 0 {
|
||
|
copy(v[:], msg)
|
||
|
copy(v[len(msg):], h.tweak[:])
|
||
|
subtle.XORBytes(dst[:], dst[:], v[:])
|
||
|
h.mul(dst)
|
||
|
copy(v[:], h.tweak[len(msg):])
|
||
|
for i := len(msg); i < blockSize; i++ {
|
||
|
v[i] = 0
|
||
|
}
|
||
|
subtle.XORBytes(dst[:], dst[:], v[:])
|
||
|
h.mul(dst)
|
||
|
for i := 0; i < len(msg); i++ {
|
||
|
v[i] = 0
|
||
|
}
|
||
|
} else {
|
||
|
subtle.XORBytes(dst[:], dst[:], h.tweak[:])
|
||
|
h.mul(dst)
|
||
|
}
|
||
|
// (|M|)₂
|
||
|
binary.BigEndian.PutUint64(v[8:], uint64(len(m)+blockSize)<<3)
|
||
|
subtle.XORBytes(dst[:], dst[:], v[:])
|
||
|
h.mul(dst)
|
||
|
}
|
||
|
|
||
|
func (h *hctr) Encrypt(ciphertext, plaintext []byte) {
|
||
|
if len(ciphertext) < len(plaintext) {
|
||
|
panic("hctr: ciphertext is smaller than plaintext")
|
||
|
}
|
||
|
if len(plaintext) < blockSize {
|
||
|
panic("hctr: plaintext length is smaller than the block size")
|
||
|
}
|
||
|
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
|
||
|
panic("hctr: invalid buffer overlap")
|
||
|
}
|
||
|
|
||
|
var z1, z2 [blockSize]byte
|
||
|
|
||
|
// a) z1 generation
|
||
|
h.uhash(plaintext[blockSize:], &z1)
|
||
|
subtle.XORBytes(z1[:], z1[:], plaintext[:blockSize])
|
||
|
// b) z2 generation
|
||
|
h.cipher.Encrypt(z2[:], z1[:])
|
||
|
// c) CTR
|
||
|
subtle.XORBytes(z1[:], z1[:], z2[:])
|
||
|
h.ctr(ciphertext[blockSize:], plaintext[blockSize:], &z1)
|
||
|
// d) first ciphertext block generation
|
||
|
h.uhash(ciphertext[blockSize:], &z1)
|
||
|
subtle.XORBytes(ciphertext, z2[:], z1[:])
|
||
|
}
|
||
|
|
||
|
func (h *hctr) Decrypt(plaintext, ciphertext []byte) {
|
||
|
if len(plaintext) < len(ciphertext) {
|
||
|
panic("hctr: plaintext is smaller than cihpertext")
|
||
|
}
|
||
|
if len(ciphertext) < blockSize {
|
||
|
panic("hctr: ciphertext length is smaller than the block size")
|
||
|
}
|
||
|
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
|
||
|
panic("hctr: invalid buffer overlap")
|
||
|
}
|
||
|
|
||
|
var z1, z2 [blockSize]byte
|
||
|
|
||
|
// a) z2 generation
|
||
|
h.uhash(ciphertext[blockSize:], &z2)
|
||
|
subtle.XORBytes(z2[:], z2[:], ciphertext[:blockSize])
|
||
|
// b) z1 generation
|
||
|
h.cipher.Decrypt(z1[:], z2[:])
|
||
|
// c) CTR
|
||
|
subtle.XORBytes(z2[:], z2[:], z1[:])
|
||
|
h.ctr(plaintext[blockSize:], ciphertext[blockSize:], &z2)
|
||
|
// d) first plaintext block generation
|
||
|
h.uhash(plaintext[blockSize:], &z2)
|
||
|
subtle.XORBytes(plaintext, z2[:], z1[:])
|
||
|
}
|
||
|
|
||
|
func (h *hctr) ctr(dst, src []byte, baseCtr *[blockSize]byte) {
|
||
|
ctr := make([]byte, blockSize)
|
||
|
num := make([]byte, blockSize)
|
||
|
i := uint64(1)
|
||
|
|
||
|
if concCipher, ok := h.cipher.(concurrentBlocks); ok {
|
||
|
batchSize := concCipher.Concurrency() * blockSize
|
||
|
if len(src) >= batchSize {
|
||
|
var ctrs []byte = make([]byte, batchSize)
|
||
|
for len(src) >= batchSize {
|
||
|
for j := 0; j < concCipher.Concurrency(); j++ {
|
||
|
// (i)₂
|
||
|
binary.BigEndian.PutUint64(num[blockSize-8:], i)
|
||
|
subtle.XORBytes(ctrs[j*blockSize:], baseCtr[:], num)
|
||
|
i++
|
||
|
}
|
||
|
concCipher.EncryptBlocks(ctrs, ctrs)
|
||
|
subtle.XORBytes(dst, src, ctrs)
|
||
|
src = src[batchSize:]
|
||
|
dst = dst[batchSize:]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for len(src) > 0 {
|
||
|
// (i)₂
|
||
|
binary.BigEndian.PutUint64(num[blockSize-8:], i)
|
||
|
subtle.XORBytes(ctr, baseCtr[:], num)
|
||
|
h.cipher.Encrypt(ctr, ctr)
|
||
|
n := subtle.XORBytes(dst, src, ctr)
|
||
|
src = src[n:]
|
||
|
dst = dst[n:]
|
||
|
i++
|
||
|
}
|
||
|
}
|