mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-26 12:16:20 +08:00
#248, rollback changes
This commit is contained in:
parent
5c38bb3add
commit
022e85231e
2
.github/workflows/test_s390x.yaml
vendored
2
.github/workflows/test_s390x.yaml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.22.x]
|
||||
go-version: [1.19.x]
|
||||
arch: [s390x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -1,47 +0,0 @@
|
||||
|
||||
// Copyright 2024 Sun Yimin. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build purego || !s390x
|
||||
|
||||
package cipher
|
||||
|
||||
import (
|
||||
_cipher "crypto/cipher"
|
||||
)
|
||||
|
||||
// A proxy of Golang cipher gcm mode.
|
||||
|
||||
// NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode
|
||||
// with the standard nonce length.
|
||||
//
|
||||
// In general, the GHASH operation performed by this implementation of GCM is not constant-time.
|
||||
// An exception is when the underlying Block was created by aes.NewCipher
|
||||
// on systems with hardware support for AES. See the crypto/aes package documentation for details.
|
||||
func NewGCM(cipher _cipher.Block) (_cipher.AEAD, error) {
|
||||
return _cipher.NewGCM(cipher)
|
||||
}
|
||||
|
||||
// NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
|
||||
// Counter Mode, which accepts nonces of the given length. The length must not
|
||||
// be zero.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing
|
||||
// cryptosystem that uses non-standard nonce lengths. All other users should use
|
||||
// NewGCM, which is faster and more resistant to misuse.
|
||||
func NewGCMWithNonceSize(cipher _cipher.Block, size int) (_cipher.AEAD, error) {
|
||||
return _cipher.NewGCMWithNonceSize(cipher, size)
|
||||
}
|
||||
|
||||
// NewGCMWithTagSize returns the given 128-bit, block cipher wrapped in Galois
|
||||
// Counter Mode, which generates tags with the given length.
|
||||
//
|
||||
// Tag sizes between 12 and 16 bytes are allowed.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing
|
||||
// cryptosystem that uses non-standard tag lengths. All other users should use
|
||||
// NewGCM, which is more resistant to misuse.
|
||||
func NewGCMWithTagSize(cipher _cipher.Block, tagSize int) (_cipher.AEAD, error) {
|
||||
return _cipher.NewGCMWithTagSize(cipher, tagSize)
|
||||
}
|
@ -1,285 +0,0 @@
|
||||
// Copyright 2024 Sun Yimin. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !purego
|
||||
|
||||
package cipher
|
||||
|
||||
import (
|
||||
_cipher "crypto/cipher"
|
||||
_subtle "crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/alias"
|
||||
"github.com/emmansun/gmsm/internal/subtle"
|
||||
)
|
||||
|
||||
// gcmAble is an interface implemented by ciphers that have a specific optimized
|
||||
// implementation of GCM, like crypto/aes. NewGCM will check for this interface
|
||||
// and return the specific AEAD if found.
|
||||
type gcmAble interface {
|
||||
NewGCM(nonceSize, tagSize int) (_cipher.AEAD, error)
|
||||
}
|
||||
|
||||
// gcmHashKey represents the 16-byte hash key required by the GHASH algorithm.
|
||||
type gcmHashKey [16]byte
|
||||
|
||||
// gcm represents a Galois Counter Mode with a specific key. See
|
||||
// https://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
|
||||
type gcm struct {
|
||||
cipher _cipher.Block
|
||||
nonceSize int
|
||||
tagSize int
|
||||
hashKey gcmHashKey
|
||||
}
|
||||
|
||||
// NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode
|
||||
// with the standard nonce length.
|
||||
//
|
||||
// In general, the GHASH operation performed by this implementation of GCM is not constant-time.
|
||||
// An exception is when the underlying Block was created by aes.NewCipher
|
||||
// on systems with hardware support for AES. See the crypto/aes package documentation for details.
|
||||
func NewGCM(cipher _cipher.Block) (_cipher.AEAD, error) {
|
||||
return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, gcmTagSize)
|
||||
}
|
||||
|
||||
// NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
|
||||
// Counter Mode, which accepts nonces of the given length. The length must not
|
||||
// be zero.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing
|
||||
// cryptosystem that uses non-standard nonce lengths. All other users should use
|
||||
// NewGCM, which is faster and more resistant to misuse.
|
||||
func NewGCMWithNonceSize(cipher _cipher.Block, size int) (_cipher.AEAD, error) {
|
||||
return newGCMWithNonceAndTagSize(cipher, size, gcmTagSize)
|
||||
}
|
||||
|
||||
// NewGCMWithTagSize returns the given 128-bit, block cipher wrapped in Galois
|
||||
// Counter Mode, which generates tags with the given length.
|
||||
//
|
||||
// Tag sizes between 12 and 16 bytes are allowed.
|
||||
//
|
||||
// Only use this function if you require compatibility with an existing
|
||||
// cryptosystem that uses non-standard tag lengths. All other users should use
|
||||
// NewGCM, which is more resistant to misuse.
|
||||
func NewGCMWithTagSize(cipher _cipher.Block, tagSize int) (_cipher.AEAD, error) {
|
||||
return newGCMWithNonceAndTagSize(cipher, gcmStandardNonceSize, tagSize)
|
||||
}
|
||||
|
||||
func newGCMWithNonceAndTagSize(cipher _cipher.Block, nonceSize, tagSize int) (_cipher.AEAD, error) {
|
||||
if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize {
|
||||
return nil, errors.New("cipher: incorrect tag size given to GCM")
|
||||
}
|
||||
|
||||
if nonceSize <= 0 {
|
||||
return nil, errors.New("cipher: the nonce can't have zero length, or the security of the key will be immediately compromised")
|
||||
}
|
||||
|
||||
if cipher, ok := cipher.(gcmAble); ok {
|
||||
return cipher.NewGCM(nonceSize, tagSize)
|
||||
}
|
||||
|
||||
if cipher.BlockSize() != gcmBlockSize {
|
||||
return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
|
||||
}
|
||||
|
||||
var hk gcmHashKey
|
||||
cipher.Encrypt(hk[:], hk[:])
|
||||
|
||||
g := &gcm{cipher: cipher, nonceSize: nonceSize, tagSize: tagSize, hashKey: hk}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
const (
|
||||
gcmBlockSize = 16
|
||||
gcmTagSize = 16
|
||||
gcmMinimumTagSize = 12 // NIST SP 800-38D recommends tags with 12 or more bytes.
|
||||
gcmStandardNonceSize = 12
|
||||
)
|
||||
|
||||
func (g *gcm) NonceSize() int {
|
||||
return g.nonceSize
|
||||
}
|
||||
|
||||
func (g *gcm) Overhead() int {
|
||||
return g.tagSize
|
||||
}
|
||||
|
||||
// gcmCount represents a 16-byte big-endian count value.
|
||||
type gcmCount [16]byte
|
||||
|
||||
// inc increments the rightmost 32-bits of the count value by 1.
|
||||
func (x *gcmCount) inc() {
|
||||
binary.BigEndian.PutUint32(x[len(x)-4:], binary.BigEndian.Uint32(x[len(x)-4:])+1)
|
||||
}
|
||||
|
||||
// ghash uses the GHASH algorithm to hash data with the given key. The initial
|
||||
// hash value is given by hash which will be updated with the new hash value.
|
||||
// The length of data must be a multiple of 16-bytes.
|
||||
//
|
||||
//go:noescape
|
||||
func ghash(key *gcmHashKey, hash *[16]byte, data []byte)
|
||||
|
||||
// paddedGHASH pads data with zeroes until its length is a multiple of
|
||||
// 16-bytes. It then calculates a new value for hash using the GHASH algorithm.
|
||||
func (g *gcm) paddedGHASH(hash *[16]byte, data []byte) {
|
||||
siz := len(data) &^ 0xf // align size to 16-bytes
|
||||
if siz > 0 {
|
||||
ghash(&g.hashKey, hash, data[:siz])
|
||||
data = data[siz:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
var s [16]byte
|
||||
copy(s[:], data)
|
||||
ghash(&g.hashKey, hash, s[:])
|
||||
}
|
||||
}
|
||||
|
||||
// gcmLengths writes len0 || len1 as big-endian values to a 16-byte array.
|
||||
func gcmLengths(len0, len1 uint64) [16]byte {
|
||||
v := [16]byte{}
|
||||
binary.BigEndian.PutUint64(v[0:], len0)
|
||||
binary.BigEndian.PutUint64(v[8:], len1)
|
||||
return v
|
||||
}
|
||||
|
||||
// auth calculates GHASH(ciphertext, additionalData), masks the result with
|
||||
// tagMask and writes the result to out.
|
||||
func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]byte) {
|
||||
var hash [16]byte
|
||||
g.paddedGHASH(&hash, additionalData)
|
||||
g.paddedGHASH(&hash, ciphertext)
|
||||
lens := gcmLengths(uint64(len(additionalData))*8, uint64(len(ciphertext))*8)
|
||||
g.paddedGHASH(&hash, lens[:])
|
||||
|
||||
copy(out, hash[:])
|
||||
for i := range out {
|
||||
out[i] ^= tagMask[i]
|
||||
}
|
||||
}
|
||||
|
||||
// deriveCounter computes the initial GCM counter state from the given nonce.
|
||||
// See NIST SP 800-38D, section 7.1. This assumes that counter is filled with
|
||||
// zeros on entry.
|
||||
func (g *gcm) deriveCounter(nonce []byte) gcmCount {
|
||||
// GCM has two modes of operation with respect to the initial counter
|
||||
// state: a "fast path" for 96-bit (12-byte) nonces, and a "slow path"
|
||||
// for nonces of other lengths. For a 96-bit nonce, the nonce, along
|
||||
// with a four-byte big-endian counter starting at one, is used
|
||||
// directly as the starting counter. For other nonce sizes, the counter
|
||||
// is computed by passing it through the GHASH function.
|
||||
var counter gcmCount
|
||||
if len(nonce) == gcmStandardNonceSize {
|
||||
copy(counter[:], nonce)
|
||||
counter[gcmBlockSize-1] = 1
|
||||
} else {
|
||||
var hash [16]byte
|
||||
g.paddedGHASH(&hash, nonce)
|
||||
lens := gcmLengths(0, uint64(len(nonce))*8)
|
||||
g.paddedGHASH(&hash, lens[:])
|
||||
copy(counter[:], hash[:])
|
||||
}
|
||||
return counter
|
||||
}
|
||||
|
||||
// counterCrypt crypts in to out using g.cipher in counter mode.
|
||||
func (g *gcm) counterCrypt(out, in []byte, counter *gcmCount) {
|
||||
var mask [gcmBlockSize]byte
|
||||
|
||||
for len(in) >= gcmBlockSize {
|
||||
g.cipher.Encrypt(mask[:], counter[:])
|
||||
counter.inc()
|
||||
|
||||
subtle.XORBytes(out, in, mask[:])
|
||||
out = out[gcmBlockSize:]
|
||||
in = in[gcmBlockSize:]
|
||||
}
|
||||
|
||||
if len(in) > 0 {
|
||||
g.cipher.Encrypt(mask[:], counter[:])
|
||||
counter.inc()
|
||||
subtle.XORBytes(out, in, mask[:])
|
||||
}
|
||||
}
|
||||
|
||||
// Seal encrypts and authenticates plaintext. See the cipher.AEAD interface for
|
||||
// details.
|
||||
func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte {
|
||||
if len(nonce) != g.nonceSize {
|
||||
panic("crypto/cipher: incorrect nonce length given to GCM")
|
||||
}
|
||||
if uint64(len(plaintext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize()) {
|
||||
panic("crypto/cipher: message too large for GCM")
|
||||
}
|
||||
|
||||
ret, out := alias.SliceForAppend(dst, len(plaintext)+g.tagSize)
|
||||
if alias.InexactOverlap(out[:len(plaintext)], plaintext) {
|
||||
panic("crypto/cipher: invalid buffer overlap")
|
||||
}
|
||||
|
||||
counter := g.deriveCounter(nonce)
|
||||
|
||||
var tagMask [gcmBlockSize]byte
|
||||
g.cipher.Encrypt(tagMask[:], counter[:])
|
||||
counter.inc()
|
||||
|
||||
var tagOut [gcmTagSize]byte
|
||||
g.counterCrypt(out, plaintext, &counter)
|
||||
g.auth(tagOut[:], out[:len(plaintext)], data, &tagMask)
|
||||
copy(out[len(plaintext):], tagOut[:])
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Open authenticates and decrypts ciphertext. See the cipher.AEAD interface
|
||||
// for details.
|
||||
func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
||||
if len(nonce) != g.nonceSize {
|
||||
panic("crypto/cipher: incorrect nonce length given to GCM")
|
||||
}
|
||||
// Sanity check to prevent the authentication from always succeeding if an implementation
|
||||
// leaves tagSize uninitialized, for example.
|
||||
if g.tagSize < gcmMinimumTagSize {
|
||||
panic("crypto/cipher: incorrect GCM tag size")
|
||||
}
|
||||
if len(ciphertext) < g.tagSize {
|
||||
return nil, errOpen
|
||||
}
|
||||
if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(g.cipher.BlockSize())+uint64(g.tagSize) {
|
||||
return nil, errOpen
|
||||
}
|
||||
|
||||
tag := ciphertext[len(ciphertext)-g.tagSize:]
|
||||
ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
|
||||
|
||||
counter := g.deriveCounter(nonce)
|
||||
|
||||
var tagMask [gcmBlockSize]byte
|
||||
g.cipher.Encrypt(tagMask[:], counter[:])
|
||||
counter.inc()
|
||||
|
||||
var expectedTag [gcmTagSize]byte
|
||||
g.auth(expectedTag[:], ciphertext, data, &tagMask)
|
||||
|
||||
ret, out := alias.SliceForAppend(dst, len(ciphertext))
|
||||
if alias.InexactOverlap(out, ciphertext) {
|
||||
panic("crypto/cipher: invalid buffer overlap")
|
||||
}
|
||||
|
||||
if _subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
|
||||
// The AESNI code decrypts and authenticates concurrently, and
|
||||
// so overwrites dst in the event of a tag mismatch. That
|
||||
// behavior is mimicked here in order to be consistent across
|
||||
// platforms.
|
||||
for i := range out {
|
||||
out[i] = 0
|
||||
}
|
||||
return nil, errOpen
|
||||
}
|
||||
|
||||
g.counterCrypt(out, ciphertext, &counter)
|
||||
return ret, nil
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func ghash(key *gcmHashKey, hash *[16]byte, data []byte)
|
||||
TEXT ·ghash(SB),NOSPLIT,$32-40
|
||||
MOVD $65, R0 // GHASH function code
|
||||
MOVD key+0(FP), R2
|
||||
LMG (R2), R6, R7
|
||||
MOVD hash+8(FP), R8
|
||||
LMG (R8), R4, R5
|
||||
MOVD $params-32(SP), R1
|
||||
STMG R4, R7, (R1)
|
||||
LMG data+16(FP), R2, R3 // R2=base, R3=len
|
||||
loop:
|
||||
KIMD R0, R2 // compute intermediate message digest (KIMD)
|
||||
BVS loop // branch back if interrupted
|
||||
MVC $16, (R1), (R8)
|
||||
MOVD $0, R0
|
||||
RET
|
@ -2,14 +2,13 @@ package cipher_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_cipher "crypto/cipher"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/emmansun/gmsm/cipher"
|
||||
"github.com/emmansun/gmsm/internal/cryptotest"
|
||||
"github.com/emmansun/gmsm/sm4"
|
||||
)
|
||||
@ -244,7 +243,7 @@ func TestSM4GCM(t *testing.T) {
|
||||
ad, _ := hex.DecodeString(test.ad)
|
||||
tagSize := (len(test.result) - len(test.plaintext)) / 2
|
||||
|
||||
var sm4gcm _cipher.AEAD
|
||||
var sm4gcm cipher.AEAD
|
||||
switch {
|
||||
// Handle non-standard tag sizes
|
||||
case tagSize != 16:
|
||||
@ -441,18 +440,18 @@ func TestGCMAEAD(t *testing.T) {
|
||||
|
||||
// Test GCM with the current AES block with the standard nonce and tag
|
||||
// sizes.
|
||||
cryptotest.TestAEAD(t, func() (_cipher.AEAD, error) { return cipher.NewGCM(block) })
|
||||
cryptotest.TestAEAD(t, func() (cipher.AEAD, error) { return cipher.NewGCM(block) })
|
||||
|
||||
// Test non-standard tag sizes.
|
||||
t.Run("MinTagSize", func(t *testing.T) {
|
||||
cryptotest.TestAEAD(t, func() (_cipher.AEAD, error) { return cipher.NewGCMWithTagSize(block, minTagSize) })
|
||||
cryptotest.TestAEAD(t, func() (cipher.AEAD, error) { return cipher.NewGCMWithTagSize(block, minTagSize) })
|
||||
})
|
||||
|
||||
// Test non-standard nonce sizes.
|
||||
for _, nonceSize := range []int{1, 16, 100} {
|
||||
t.Run(fmt.Sprintf("NonceSize-%d", nonceSize), func(t *testing.T) {
|
||||
|
||||
cryptotest.TestAEAD(t, func() (_cipher.AEAD, error) { return cipher.NewGCMWithNonceSize(block, nonceSize) })
|
||||
cryptotest.TestAEAD(t, func() (cipher.AEAD, error) { return cipher.NewGCMWithNonceSize(block, nonceSize) })
|
||||
})
|
||||
}
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user