diff --git a/.github/workflows/test_s390x.yaml b/.github/workflows/test_s390x.yaml index 80e5126..abd2733 100644 --- a/.github/workflows/test_s390x.yaml +++ b/.github/workflows/test_s390x.yaml @@ -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: diff --git a/cipher/gcm.go b/cipher/gcm.go deleted file mode 100644 index 8237b62..0000000 --- a/cipher/gcm.go +++ /dev/null @@ -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) -} diff --git a/cipher/gcm_s390x.go b/cipher/gcm_s390x.go deleted file mode 100644 index 9b4e5b8..0000000 --- a/cipher/gcm_s390x.go +++ /dev/null @@ -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 -} diff --git a/cipher/gcm_s390x.s b/cipher/gcm_s390x.s deleted file mode 100644 index 658c8b5..0000000 --- a/cipher/gcm_s390x.s +++ /dev/null @@ -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 diff --git a/cipher/gcm_sm4_test.go b/cipher/gcm_sm4_test.go index 04bb6e9..e3796e4 100644 --- a/cipher/gcm_sm4_test.go +++ b/cipher/gcm_sm4_test.go @@ -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) }) }) } })