Compare commits

...

68 Commits

Author SHA1 Message Date
Sun Yimin
7b75b6b26b
update disclaimer 2025-04-18 08:50:39 +08:00
Sun Yimin
5aacbc2011
Merge pull request #324 from hrimfaxi/main
sm9: Prevent PublicKey() returning nil after unmarshaling master priavate key
2025-04-15 17:31:55 +08:00
YuanHongYe
fe532e12b4 sm9: Prevent PublicKey() returning nil after unmarshaling master private key
During unmarshaling of SignMasterPrivateKey and EncryptMasterPrivateKey,
now generate the corresponding public key.
This ensures that PublicKey() does not return nil.

Test cases included to validate the changes.

Signed-off-by: YuanHongYe <yuanhongye@chinatelecom.cn>
2025-04-15 17:01:27 +08:00
Sun Yimin
1dc82305e4
Merge pull request #323 from emmansun/dependabot/go_modules/golang.org/x/crypto-0.37.0
build(deps): bump golang.org/x/crypto from 0.36.0 to 0.37.0
2025-04-08 09:28:54 +08:00
dependabot[bot]
ed256a9ea3
build(deps): bump golang.org/x/crypto from 0.36.0 to 0.37.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.36.0 to 0.37.0.
- [Commits](https://github.com/golang/crypto/compare/v0.36.0...v0.37.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.37.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-08 00:52:59 +00:00
Sun Yimin
d8c6788e8f
sm2, sm2ec: remove useless codes since go 1.19 2025-04-07 13:10:09 +08:00
Sun Yimin
0ef30b3ab5
internal/zuc: supplement comments 2025-04-03 10:40:06 +08:00
Sun Yimin
d3eece5560
cfca: fix test cases #322 2025-04-01 17:06:47 +08:00
Sun Yimin
0af92d8e48
smx509: CFCA CSR supports RSA keys #322 2025-04-01 17:02:13 +08:00
Sun Yimin
bf644fbb4e
docs/zuc: add seekable stream part 2025-03-28 17:19:19 +08:00
Sun Yimin
359b46453b
internal/zuc,zuc: eea seakable stream support zuc states cache per bucket #321 2025-03-28 16:53:29 +08:00
Sun Yimin
b8d52dd11d
internal/sm9/bn256: make gfP.Unmarshal constant time 2025-03-26 16:37:04 +08:00
Sun Yimin
9ea8293d10
internal/sm2ec/fiat: fix error message 2025-03-26 15:20:30 +08:00
Sun Yimin
e8a847e005
internal/sm9/bn256: remove useless code 2025-03-26 09:49:30 +08:00
Sun Yimin
a7c4473a48
internal/sm9/bn256: reduce big.Int usage 2025-03-26 09:30:39 +08:00
Sun Yimin
11d0438cc4
internal/sm9: reduce usage of big.Int 2025-03-25 17:19:10 +08:00
Sun Yimin
88df15c64c
sm9: implement crypto.Singer crypto.Decrypter interface 2025-03-25 14:58:16 +08:00
Sun Yimin
3eea15b3b8
internal/sm9,sm9: refactor all keys 2025-03-25 11:10:53 +08:00
Sun Yimin
dd69d32930
drbg: replace with for range 2025-03-25 08:49:56 +08:00
Sun Yimin
a84fec09af
internal/cpu: move to internal/deps/cpu #310 2025-03-21 10:04:35 +08:00
Sun Yimin
c43e0488a6
cbcmac: fix typo 2025-03-21 08:38:59 +08:00
Sun Yimin
a4affe6006
cbcmac,padding: supplement/update comments 2025-03-20 16:34:19 +08:00
Sun Yimin
3cc92436ee
padding: fix "Size computation for allocation may overflow" 2025-03-19 11:16:26 +08:00
Sun Yimin
93c965f3c1
cipher: move xts detail implementation to internal #320 2025-03-19 10:21:52 +08:00
Sun Yimin
d8eb166dfc
cbcmac: enable provided padding for EMAC/ANSI Retail MAC/MAC-DES/LMAC #319 2025-03-19 10:10:00 +08:00
Sun Yimin
2d3329a2ea
padding: missing PaddingFunc #319 2025-03-18 11:44:45 +08:00
Sun Yimin
f41a5c69e7
cbcmac: CBCMAC enable provided padding method #319 2025-03-18 11:41:04 +08:00
Sun Yimin
cf027254dc
update README #319 2025-03-18 11:27:28 +08:00
Sun Yimin
069babe703
padding: implement ISO IEC9797-1 padding method 3 #319 2025-03-18 11:21:48 +08:00
Sun Yimin
5734e67634
internal/cpu,internal/sm9: refactor and fix 2025-03-17 17:18:58 +08:00
Sun Yimin
82ccb95527
sm9,internal/sm9: update comments #314 2025-03-14 15:26:34 +08:00
Sun Yimin
e79aab4935
smx509: better handling of weird encodings #316 2025-03-14 08:44:52 +08:00
Sun Yimin
c32a9849f8
sm9: refactoring #314 2025-03-13 16:50:28 +08:00
Sun Yimin
7ec46d700d
internal/subtle: remove xor related codes #315 2025-03-13 15:20:05 +08:00
Sun Yimin
7a5253bfb5
change to use go's own XORBytes function #315 2025-03-13 15:15:46 +08:00
Sun Yimin
d6f18a2cbf
fix CI 2025-03-13 14:44:56 +08:00
Sun Yimin
bdb169b06b
sm9: refactoring, do not expose bn256 types to caller #314 2025-03-13 14:14:45 +08:00
Sun Yimin
43ffd49e2f
sm9: refactoring, do not expose bn256 types to caller #314 2025-03-13 13:46:14 +08:00
Sun Yimin
e9692d23ab
zuc: remove useless constants 2025-03-11 16:26:41 +08:00
Sun Yimin
21f96e536b
internal/zuc: fix build error 2025-03-11 16:13:59 +08:00
Sun Yimin
a49eecd572
zuc: move implementation detail to internal 2025-03-11 16:11:18 +08:00
Sun Yimin
537c80a28b
sm3: move implementation detail to internal 2025-03-11 14:02:47 +08:00
Sun Yimin
5edcb0f966
sm4: move implementation detail to internal 2025-03-11 11:43:49 +08:00
Sun Yimin
65a69ad83c
internal/bigmod: explicitly clear expanded limbs on reset #313 2025-03-11 08:58:16 +08:00
Sun Yimin
71b196a5ac
Merge pull request #312 from emmansun/dependabot/go_modules/golang.org/x/crypto-0.36.0
build(deps): bump golang.org/x/crypto from 0.35.0 to 0.36.0
2025-03-11 08:20:58 +08:00
dependabot[bot]
500cb8a418
build(deps): bump golang.org/x/crypto from 0.35.0 to 0.36.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.35.0 to 0.36.0.
- [Commits](https://github.com/golang/crypto/compare/v0.35.0...v0.36.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 23:41:37 +00:00
Sun Yimin
762cbbf0c6
ci: check macOs 2025-03-05 15:09:15 +08:00
Sun Yimin
9d6f8087f9
internal/cpu: correct import #310 2025-03-05 15:03:21 +08:00
Sun Yimin
a6d8014ac9
internal/cpu: missing one #310 2025-03-05 14:58:28 +08:00
Sun Yimin
d7a6169fbf
internal/cpu: import codes from golang.org/x/sys/cpu #310 2025-03-05 14:54:51 +08:00
Sun Yimin
67f187b1d3
sm4: define & use type KeySizeError 2025-03-05 10:56:13 +08:00
Sun Yimin
759bb4c0b9
internal/sm2ec: make SetBytes constant time, use latest golang features #309 2025-03-04 11:30:20 +08:00
Sun Yimin
5ade794e6b
internal/sm2ec: make SetBytes constant time #309 2025-03-04 08:59:51 +08:00
Sun Yimin
89962cf1e3
smx509: avoid crypto/rand.Int to generate serial number #308 2025-03-04 08:48:18 +08:00
Sun Yimin
d433e416fa
ci: enable code coverage 2025-02-27 13:08:09 +08:00
Sun Yimin
2c0f5f68fc
smx509: add support for PKCS8/PKIX X25519 key encodings #210 2025-02-27 11:44:53 +08:00
Sun Yimin
3d4dd002a4
smx509: add new CRL parser, deprecate old one #40 2025-02-27 10:22:19 +08:00
Sun Yimin
9ba88a32a4
smx509: use bytes.Clone 2025-02-27 08:16:07 +08:00
Sun Yimin
0bb54adc1e
cipher: use new functions 2025-02-26 11:46:48 +08:00
Sun Yimin
a98b806453
padding: use new function clear() 2025-02-26 11:41:47 +08:00
Sun Yimin
51a003b022
sm3: remove duplicated code 2025-02-26 11:16:47 +08:00
Sun Yimin
dc1c5806c9
internal/bigmod: use clear() 2025-02-26 10:50:35 +08:00
Sun Yimin
869be23867
sm4: remove unused package 2025-02-26 10:26:29 +08:00
Sun Yimin
8a25134c82
sm4: fallback ctr change 2025-02-26 10:24:46 +08:00
Sun Yimin
27e7ceacbc
sm4: use new functions: clear(), bytes.Clone() 2025-02-26 10:19:56 +08:00
Sun Yimin
ec8580b01f
sm2: fix go1.24 test issue 2025-02-26 09:46:03 +08:00
Sun Yimin
2192b702b0
upgrade github action to 1.23+ 2025-02-26 08:50:00 +08:00
Sun Yimin
d03a8fba3f
upgrade to go 1.23 2025-02-26 08:46:40 +08:00
311 changed files with 10473 additions and 6418 deletions

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
goVer: ['1.18', '1.19', '1.21', '1.22', '1.23'] goVer: ['1.23', '1.24']
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -23,19 +23,19 @@ jobs:
go-version: ${{ matrix.goVer }} go-version: ${{ matrix.goVer }}
- name: Test with Coverage - name: Test with Coverage
if: ${{ matrix.goVer == '1.18' }} if: ${{ matrix.goVer == '1.23' }}
run: go test -coverpkg=./... -v -short -race -coverprofile=coverage1.txt -covermode=atomic ./... run: go test -coverpkg=./... -v -short -race -coverprofile=coverage1.txt -covermode=atomic ./...
env: env:
GODEBUG: x509sha1=1 GODEBUG: x509sha1=1
- name: Test Generic with Coverage - name: Test Generic with Coverage
if: ${{ matrix.goVer == '1.18' }} if: ${{ matrix.goVer == '1.23' }}
run: go test -coverpkg=./... -v -short -tags purego -coverprofile=coverage2.txt -covermode=atomic ./... run: go test -coverpkg=./... -v -short -tags purego -coverprofile=coverage2.txt -covermode=atomic ./...
env: env:
GODEBUG: x509sha1=1 GODEBUG: x509sha1=1
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
if: ${{ matrix.goVer == '1.18' }} if: ${{ matrix.goVer == '1.23' }}
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v5
with: with:
files: ./coverage1.txt,./coverage2.txt files: ./coverage1.txt,./coverage2.txt
@ -48,19 +48,19 @@ jobs:
FORCE_SM4BLOCK_AESNI: 1 FORCE_SM4BLOCK_AESNI: 1
- name: Test only - name: Test only
if: ${{ matrix.goVer != '1.18' }} if: ${{ matrix.goVer != '1.23' }}
run: go test -short ./... run: go test -short ./...
env: env:
GODEBUG: x509sha1=1 GODEBUG: x509sha1=1
- name: Test Generic only - name: Test Generic only
if: ${{ matrix.goVer != '1.18' && matrix.goVer != '1.23' }} if: ${{ matrix.goVer != '1.23' }}
run: go test -short -tags purego ./... run: go test -short -tags purego ./...
env: env:
GODEBUG: x509sha1=1 GODEBUG: x509sha1=1
- name: Test Plugin only - name: Test Plugin only
if: ${{ matrix.goVer != '1.18' }} if: ${{ matrix.goVer == '1.23' }}
run: go test -short -tags plugin ./... run: go test -short -tags plugin ./...
env: env:
GODEBUG: x509sha1=1 GODEBUG: x509sha1=1

View File

@ -16,7 +16,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version: 1.22 go-version: 1.23
- name: Build - name: Build
run: go build -v ./... run: go build -v ./...

View File

@ -14,7 +14,7 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.19.x] go-version: [1.23.x]
arch: [ppc64le] arch: [ppc64le]
ppc64: [power8] ppc64: [power8]
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -36,30 +36,6 @@ jobs:
GOARCH: ${{ matrix.arch }} GOARCH: ${{ matrix.arch }}
GOPPC64: ${{ matrix.ppc64 }} GOPPC64: ${{ matrix.ppc64 }}
- name: Test bn256
run: go test -v ./sm9/bn256/...
env:
GOARCH: ${{ matrix.arch }}
GOPPC64: ${{ matrix.ppc64 }}
- name: Test ZUC
run: go test -v ./zuc/...
env:
GOARCH: ${{ matrix.arch }}
GOPPC64: ${{ matrix.ppc64 }}
- name: Test SM3
run: go test -v ./sm3/...
env:
GOARCH: ${{ matrix.arch }}
GOPPC64: ${{ matrix.ppc64 }}
- name: Test SM4
run: go test -v -short ./sm4/...
env:
GOARCH: ${{ matrix.arch }}
GOPPC64: ${{ matrix.ppc64 }}
- name: Test Cipher - name: Test Cipher
run: go test -v -short ./cipher/... run: go test -v -short ./cipher/...
env: env:

View File

@ -14,7 +14,7 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.18.x] go-version: [1.23.x]
arch: [arm64] arch: [arm64]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -14,7 +14,7 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.22.x] go-version: [1.23.x]
arch: [riscv64] arch: [riscv64]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -14,7 +14,7 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.19.x] go-version: [1.23.x]
arch: [s390x] arch: [s390x]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -34,11 +34,6 @@ jobs:
env: env:
GOARCH: ${{ matrix.arch }} GOARCH: ${{ matrix.arch }}
- name: Test sm3
run: go test -v ./sm3/...
env:
GOARCH: ${{ matrix.arch }}
- name: Test Cipher - name: Test Cipher
run: go test -v -short ./cipher/... run: go test -v -short ./cipher/...
env: env:

View File

@ -14,7 +14,7 @@ jobs:
test: test:
strategy: strategy:
matrix: matrix:
go-version: [1.18.x] go-version: [1.23.x]
arch: [arm64] arch: [arm64]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -1,23 +1,27 @@
# 免责 # 免责
## 声明 ## 版权声明
本项目(gmsm)代码的版权归[Sun Yimin](mailto:emman.sun@foxmail.com)所有,并按照[MIT](https://github.com/emmansun/gmsm/blob/main/LICENSE)进行分发。使用本项目的代码时,必须遵守相应的许可证条款。
任何直接或间接因使用本项目(gmsm)的任何内容所导致的全部后果与本项目(gmsm)的开发者无关,若使用者无法对使用本项目(gmsm)后的任何后果负责,请不要使用本项目(gmsm)的任何内容。若该项目(gmsm)的任何内容侵犯了您的法律权益,请联系 emman.sun@foxmail.com作者会第一时间删除侵权内容。 ## 免责条款
## 认证 1. **无担保声明**
本项目以“原样”提供,不提供任何形式的明示或暗示担保,包括但不限于对适销性、特定用途适用性和非侵权性的担保。
本项目(gmsm)**没有**经过相关机构资质认证。 2. **责任限制**
对于因使用或无法使用本项目代码而导致的任何直接、间接、偶然、特殊、典型或惩罚性损害(包括但不限于数据丢失、利润损失、业务中断等),项目作者或贡献者概不负责。
## 保证 3. **使用风险**
使用本项目代码的风险由使用者自行承担。使用者在使用本项目时,应自行评估代码的适用性和安全性,并根据实际需求进行测试和调整。
本项目(gmsm)不提供任何形式的保证。所有与使用本项目(gmsm)软件库及源代码相关的直接或间接风险均由用户自行承担。本项目(gmsm)提供的所有代码,并不对性能、适用性、适销性及其他方面提供任何保证。 4. **第三方依赖**
本项目可能依赖于第三方库或服务,这些库或服务的使用应遵守其各自的许可证条款。项目作者或贡献者不对第三方库或服务的稳定性、安全性或合法性负责。
本项目(gmsm)实现的内容可能包含不准确性或错误,我们不对内容的准确性进行保证。如果您发现本项目(gmsm)内容包含错误,请直接提交错误至[issue tracker](https://github.com/emmansun/gmsm/issues),或者提交代码至[pull requests](https://github.com/emmansun/gmsm/pulls)。 5. **法律合规**
使用本项目代码时,使用者应确保遵守所在国家或地区的法律法规。如因使用本项目代码而违反相关法律,使用者需自行承担法律责任。
## 您的行为 6. **产品认证**
本项目(gmsm)**没有**经过相关机构(包括但不限于**国家密码管理局商用密码检测中心**)认证,也不承诺会进行相关认证工作。
当您以任何方式使用本项目(gmsm)软件库及源代码时,说明您已经同意并接受本页面的所有信息。 7. **修改与分发**
如果您修改本项目代码并重新分发,请确保保留上述版权声明和免责条款,并明确标注您的修改内容。
## 联系方式
联系邮箱emman.sun@foxmail.com相关事务请发函至该邮箱

View File

@ -43,7 +43,7 @@ Go语言商用密码软件简称**GMSM**,一个安全、高性能、易于
- **SMX509** - Go语言X509包的分支加入了商用密码支持。 - **SMX509** - Go语言X509包的分支加入了商用密码支持。
- **PADDING** - 一些填充方法实现(非常量时间运行):**pkcs7**,这是当前主要使用的填充方式,对应**GB/T 17964-2021**的附录C.2 填充方法 1**iso9797m2**,对应**GB/T 17964-2021**的附录C.3 填充方法 2**ansix923**对应ANSI X9.23标准。**GB/T 17964-2021**的附录C.4 填充方法 3目前没有实现,它对应ISO/IEC_9797-1 padding method 3,如有使用需求,可以考虑实现 - **PADDING** - 一些填充方法实现(非常量时间运行):**pkcs7**,这是当前主要使用的填充方式,对应**GB/T 17964-2021**的附录C.2 填充方法 1**iso9797m2**,对应**GB/T 17964-2021**的附录C.3 填充方法 2**ansix923**对应ANSI X9.23标准。**GB/T 17964-2021**的附录C.4 填充方法 3对应ISO/IEC_9797-1 padding method 3。
- **PKCS7** - [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7) 项目该项目已于2024年2月10日被归档的分支加入了商用密码支持。 - **PKCS7** - [mozilla-services/pkcs7](https://github.com/mozilla-services/pkcs7) 项目该项目已于2024年2月10日被归档的分支加入了商用密码支持。

View File

@ -7,16 +7,16 @@ package cbcmac
import ( import (
"crypto/cipher" "crypto/cipher"
"crypto/subtle"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/padding" "github.com/emmansun/gmsm/padding"
) )
// Reference: GB/T 15821.1-2020 Security techniques // Reference: GB/T 15821.1-2020 Security techniques
// Message authentication codes - Part 1: Mechanisms using block ciphers // Message authentication codes - Part 1: Mechanisms using block ciphers
// BockCipherMAC is the interface that wraps the basic MAC method. // BlockCipherMAC is the interface that wraps the basic MAC method.
type BockCipherMAC interface { type BlockCipherMAC interface {
// Size returns the MAC value's number of bytes. // Size returns the MAC value's number of bytes.
Size() int Size() int
@ -33,14 +33,21 @@ type cbcmac struct {
size int size int
} }
// NewCBCMAC returns a CBC-MAC instance that implements the MAC with the given block cipher. // NewCBCMAC returns a CBC-MAC (GB/T 15821.1-2020 MAC scheme 1) instance that
// The padding scheme is ISO/IEC 9797-1 method 2. // implements the MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
// GB/T 15821.1-2020 MAC scheme 1 func NewCBCMAC(b cipher.Block, size int) BlockCipherMAC {
func NewCBCMAC(b cipher.Block, size int) BockCipherMAC { return NewCBCMACWithPadding(b, size, padding.NewISO9797M2Padding)
}
// NewCBCMACWithPadding creates a new CBC-MAC (Cipher Block Chaining Message Authentication Code)
// with the specified block cipher, MAC size, and padding function. The MAC size must be greater
// than 0 and less than or equal to the block size of the cipher. If the size is invalid, the
// function will panic. The padding function is used to pad the input to the block size of the cipher.
func NewCBCMACWithPadding(b cipher.Block, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
if size <= 0 || size > b.BlockSize() { if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size") panic("cbcmac: invalid size")
} }
return &cbcmac{b: b, pad: padding.NewISO9797M2Padding(uint(b.BlockSize())), size: size} return &cbcmac{b: b, pad: newPaddingFunc(uint(b.BlockSize())), size: size}
} }
func (c *cbcmac) Size() int { func (c *cbcmac) Size() int {
@ -68,10 +75,14 @@ type emac struct {
size int size int
} }
// NewEMAC returns an EMAC instance that implements MAC with the given block cipher. // NewEMAC returns an EMAC (GB/T 15821.1-2020 MAC scheme 2) instance that
// The padding scheme is ISO/IEC 9797-1 method 2. // implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
// GB/T 15821.1-2020 MAC scheme 2 func NewEMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BlockCipherMAC {
func NewEMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BockCipherMAC { return NewEMACWithPadding(creator, key1, key2, size, padding.NewISO9797M2Padding)
}
// NewEMACWithPadding creates a new instance of EMAC (Encrypted Message Authentication Code) with padding.
func NewEMACWithPadding(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
var b1, b2 cipher.Block var b1, b2 cipher.Block
var err error var err error
if b1, err = creator(key1); err != nil { if b1, err = creator(key1); err != nil {
@ -83,7 +94,7 @@ func NewEMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte,
if b2, err = creator(key2); err != nil { if b2, err = creator(key2); err != nil {
panic(err) panic(err)
} }
return &emac{pad: padding.NewISO9797M2Padding(uint(b1.BlockSize())), b1: b1, b2: b2, size: size} return &emac{pad: newPaddingFunc(uint(b1.BlockSize())), b1: b1, b2: b2, size: size}
} }
func (e *emac) Size() int { func (e *emac) Size() int {
@ -105,11 +116,15 @@ func (e *emac) MAC(src []byte) []byte {
type ansiRetailMAC emac type ansiRetailMAC emac
// NewANSIRetailMAC returns an ANSI Retail MAC instance that implements MAC with the given block cipher. // NewANSIRetailMAC returns an ANSI Retail MAC (GB/T 15821.1-2020 MAC scheme 3) instance that
// The padding scheme is ISO/IEC 9797-1 method 2. // implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
// GB/T 15821.1-2020 MAC scheme 3 func NewANSIRetailMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BlockCipherMAC {
func NewANSIRetailMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BockCipherMAC { return NewANSIRetailMACWithPadding(creator, key1, key2, size, padding.NewISO9797M2Padding)
return (*ansiRetailMAC)(NewEMAC(creator, key1, key2, size).(*emac)) }
// NewANSIRetailMACWithPadding creates a new ANSI Retail MAC with padding.
func NewANSIRetailMACWithPadding(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
return (*ansiRetailMAC)(NewEMACWithPadding(creator, key1, key2, size, newPaddingFunc).(*emac))
} }
func (e *ansiRetailMAC) Size() int { func (e *ansiRetailMAC) Size() int {
@ -136,10 +151,14 @@ type macDES struct {
size int size int
} }
// NewMACDES returns a MAC-DES instance that implements MAC with the given block cipher. // NewMACDES returns a MAC-DES (GB/T 15821.1-2020 MAC scheme 4) instance that
// The padding scheme is ISO/IEC 9797-1 method 2. // implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
// GB/T 15821.1-2020 MAC scheme 4 func NewMACDES(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BlockCipherMAC {
func NewMACDES(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BockCipherMAC { return NewMACDESWithPadding(creator, key1, key2, size, padding.NewISO9797M2Padding)
}
// NewMACDESWithPadding creates a new BlockCipherMAC using DES encryption with padding.
func NewMACDESWithPadding(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
var b1, b2, b3 cipher.Block var b1, b2, b3 cipher.Block
var err error var err error
if b1, err = creator(key1); err != nil { if b1, err = creator(key1); err != nil {
@ -153,13 +172,13 @@ func NewMACDES(creator func(key []byte) (cipher.Block, error), key1, key2 []byte
} }
key3 := make([]byte, len(key2)) key3 := make([]byte, len(key2))
copy(key3, key2) copy(key3, key2)
for i := 0; i < len(key3); i++ { for i := range key3 {
key3[i] ^= 0xF0 key3[i] ^= 0xF0
} }
if b3, err = creator(key3); err != nil { if b3, err = creator(key3); err != nil {
panic(err) panic(err)
} }
return &macDES{pad: padding.NewISO9797M2Padding(uint(b1.BlockSize())), b1: b1, b2: b2, b3: b3, size: size} return &macDES{pad: newPaddingFunc(uint(b1.BlockSize())), b1: b1, b2: b2, b3: b3, size: size}
} }
func (m *macDES) Size() int { func (m *macDES) Size() int {
@ -194,8 +213,7 @@ type cmac struct {
len uint64 len uint64
} }
// NewCMAC returns a CMAC instance that implements MAC with the given block cipher. // NewCMAC returns a CMAC (GB/T 15821.1-2020 MAC scheme 5) instance that implements MAC with the given block cipher.
// GB/T 15821.1-2020 MAC scheme 5
// //
// Reference: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38B.pdf // Reference: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38B.pdf
func NewCMAC(b cipher.Block, size int) *cmac { func NewCMAC(b cipher.Block, size int) *cmac {
@ -334,9 +352,14 @@ type lmac struct {
size int size int
} }
// NewLMAC returns an LMAC instance that implements MAC with the given block cipher. // NewLMAC returns an LMAC (GB/T 15821.1-2020 MAC scheme 6) instance that
// GB/T 15821.1-2020 MAC scheme 6 // implements MAC with the given block cipher. The padding scheme is ISO/IEC 9797-1 method 2.
func NewLMAC(creator func(key []byte) (cipher.Block, error), key []byte, size int) BockCipherMAC { func NewLMAC(creator func(key []byte) (cipher.Block, error), key []byte, size int) BlockCipherMAC {
return NewLMACWithPadding(creator, key, size, padding.NewISO9797M2Padding)
}
// NewLMACWithPadding creates a new LMAC (Length-based Message Authentication Code) with padding.
func NewLMACWithPadding(creator func(key []byte) (cipher.Block, error), key []byte, size int, newPaddingFunc padding.NewPaddingFunc) BlockCipherMAC {
var b, b1, b2 cipher.Block var b, b1, b2 cipher.Block
var err error var err error
if b, err = creator(key); err != nil { if b, err = creator(key); err != nil {
@ -359,7 +382,7 @@ func NewLMAC(creator func(key []byte) (cipher.Block, error), key []byte, size in
panic(err) panic(err)
} }
return &lmac{b1: b1, b2: b2, pad: padding.NewISO9797M2Padding(uint(blockSize)), size: size} return &lmac{b1: b1, b2: b2, pad: newPaddingFunc(uint(blockSize)), size: size}
} }
func (l *lmac) Size() int { func (l *lmac) Size() int {
@ -385,11 +408,11 @@ type trCBCMAC struct {
size int size int
} }
// NewTRCBCMAC returns a TR-CBC-MAC instance that implements MAC with the given block cipher. // NewTRCBCMAC returns a TR-CBC-MAC (GB/T 15821.1-2020 MAC scheme 7) instance that
// GB/T 15821.1-2020 MAC scheme 7 // implements MAC with the given block cipher.
// //
// Reference: TrCBC: Another look at CBC-MAC. // Reference: TrCBC: Another look at CBC-MAC.
func NewTRCBCMAC(b cipher.Block, size int) BockCipherMAC { func NewTRCBCMAC(b cipher.Block, size int) BlockCipherMAC {
if size <= 0 || size > b.BlockSize() { if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size") panic("cbcmac: invalid size")
} }
@ -425,11 +448,10 @@ type cbcrMAC struct {
size int size int
} }
// NewCBCRMAC returns a CBCRMAC instance that implements MAC with the given block cipher. // NewCBCRMAC returns a CBCRMAC (GB/T 15821.1-2020 MAC scheme 8) instance that implements MAC with the given block cipher.
// GB/T 15821.1-2020 MAC scheme 8
// //
// Reference: CBCR: CBC MAC with rotating transformations. // Reference: CBCR: CBC MAC with rotating transformations.
func NewCBCRMAC(b cipher.Block, size int) BockCipherMAC { func NewCBCRMAC(b cipher.Block, size int) BlockCipherMAC {
if size <= 0 || size > b.BlockSize() { if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size") panic("cbcmac: invalid size")
} }

View File

@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/emmansun/gmsm/internal/cryptotest" "github.com/emmansun/gmsm/internal/cryptotest"
"github.com/emmansun/gmsm/padding"
"github.com/emmansun/gmsm/sm4" "github.com/emmansun/gmsm/sm4"
) )
@ -48,6 +49,114 @@ func TestCBCMAC(t *testing.T) {
} }
} }
func TestCBCMACWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
nil,
[]byte{0x8c, 0x33, 0x8e, 0x5a, 0x27, 0xe3, 0x49, 0xbe, 0xae, 0x39, 0x21, 0x4f, 0xed, 0xa9, 0x70, 0x99},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0x4b, 0x65, 0x53, 0xaf, 0x3c, 0x4e, 0x27, 0x44, 0x84, 0x12, 0x31, 0x5a, 0xc7, 0x84, 0x95, 0x35},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x42, 0x1a, 0xd1, 0x69, 0x0a, 0xa1, 0x52, 0xe2, 0x84, 0x6f, 0xa2, 0xa5, 0xd8, 0x34, 0x45, 0xa9},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0x71, 0xaf, 0x7e, 0x45, 0x53, 0x40, 0x4c, 0xbc, 0xc4, 0xf2, 0x97, 0x3c, 0xdb, 0xd0, 0xf0, 0x63},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x6a, 0x4a, 0x86, 0xf5, 0xb5, 0xe4, 0x68, 0xda, 0xd2, 0x7d, 0xf2, 0x5f, 0xb9, 0xd9, 0xbe, 0x16},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
block, err := sm4.NewCipher(c.key)
if err != nil {
t.Errorf("#%d: failed to create cipher: %v", i, err)
}
mac := NewCBCMACWithPadding(block, len(c.tag), c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestEMACWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key1 []byte
key2 []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
nil,
[]byte{0x2c, 0xf6, 0xed, 0xf6, 0x3c, 0xce, 0x14, 0x44, 0x89, 0xea, 0xdd, 0xf0, 0x7b, 0x49, 0x38, 0xdb},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0xe4, 0x23, 0xe3, 0x55, 0x99, 0xaf, 0xd9, 0x48, 0xae, 0xc5, 0x0b, 0xde, 0xe8, 0x38, 0xe9, 0xea},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0xf0, 0x26, 0x25, 0xce, 0xad, 0x00, 0x8d, 0x4e, 0xfb, 0xf3, 0xf0, 0xb2, 0xb0, 0xc2, 0xa7, 0x5b},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x40, 0x03, 0xba, 0x1b, 0x6a, 0xdc, 0x53, 0xa8, 0x26, 0xe8, 0x2f, 0xce, 0xa1, 0x6a, 0xfa, 0xac},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0xff, 0xd5, 0xf1, 0xf2, 0xe5, 0xed, 0xa5, 0xcb, 0xf4, 0x02, 0xd6, 0x5a, 0x5b, 0x0b, 0x19, 0x53},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
mac := NewEMACWithPadding(sm4.NewCipher, c.key1, c.key2, len(c.tag), c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestEMAC(t *testing.T) { func TestEMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B. // Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct { cases := []struct {
@ -122,6 +231,61 @@ func TestANSIRetailMAC(t *testing.T) {
} }
} }
func TestANSIRetailMACWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key1 []byte
key2 []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
nil,
[]byte{0xb4, 0x73, 0x6b, 0xe9, 0xa1, 0x74, 0xfa, 0xa3, 0x4d, 0xb1, 0xe9, 0xf1, 0xda, 0xcd, 0x5d, 0x62},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x51, 0xe9, 0x92, 0x8c, 0x22, 0x38, 0x33, 0x0c, 0x32, 0x31, 0xb8, 0x75, 0x2a, 0x9a, 0xfd, 0x7f},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0x19, 0x72, 0x47, 0x22, 0x9c, 0xe9, 0xd7, 0xb6, 0xae, 0x40, 0x5b, 0xf8, 0x85, 0xb2, 0x70, 0x57},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x7c, 0xd4, 0x8c, 0x42, 0x42, 0xe4, 0x55, 0x75, 0xe5, 0x1a, 0xaf, 0x0d, 0xcc, 0x7a, 0x20, 0x8c},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0x3c, 0x43, 0x0f, 0x1e, 0xa4, 0x3b, 0x54, 0x0c, 0x68, 0x45, 0x7e, 0x24, 0x9c, 0x46, 0xf1, 0xdb},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
mac := NewANSIRetailMACWithPadding(sm4.NewCipher, c.key1, c.key2, len(c.tag), c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestMACDES(t *testing.T) { func TestMACDES(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B. // Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct { cases := []struct {
@ -159,6 +323,61 @@ func TestMACDES(t *testing.T) {
} }
} }
func TestMACDESWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key1 []byte
key2 []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
nil,
[]byte{0x0c, 0x56, 0x00, 0x96, 0xb6, 0x09, 0xed, 0x0e, 0xaa, 0x39, 0xaf, 0xd6, 0xe2, 0x66, 0x65, 0x11},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x7e, 0x1a, 0x9a, 0x5e, 0x0e, 0xf0, 0x94, 0x7f, 0x25, 0xcb, 0x94, 0x85, 0x26, 0x1c, 0x98, 0x5c},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0x94, 0x94, 0x76, 0xd3, 0x5f, 0x17, 0x26, 0x1e, 0x1f, 0xb8, 0xc4, 0x39, 0x6d, 0x62, 0xdc, 0x05},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message for mac"),
[]byte{0x28, 0xa7, 0x0d, 0x6b, 0xcc, 0xf7, 0x44, 0x22, 0x46, 0x20, 0x58, 0xab, 0xbc, 0x27, 0xf6, 0xae},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte{0x41, 0x49, 0xd2, 0xad, 0xed, 0x94, 0x56, 0x68, 0x1e, 0xc8, 0xb5, 0x11, 0xd9, 0xe7, 0xee, 0x04},
[]byte("This is the test message "),
[]byte{0xc9, 0xd3, 0x4e, 0x16, 0xc4, 0x9a, 0xb6, 0x43, 0x57, 0xa2, 0x61, 0x8d, 0xeb, 0xd1, 0x03, 0x2f},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
mac := NewMACDESWithPadding(sm4.NewCipher, c.key1, c.key2, 16, c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestCMAC(t *testing.T) { func TestCMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B. // Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct { cases := []struct {
@ -229,6 +448,55 @@ func TestLMAC(t *testing.T) {
} }
} }
func TestLMACWithPadding(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct {
key []byte
src []byte
tag []byte
newPaddingFunc padding.NewPaddingFunc
}{
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
nil,
[]byte{0xcd, 0x7e, 0xd2, 0x79, 0x64, 0xe2, 0x57, 0xc0, 0x77, 0xf0, 0x55, 0xf8, 0xee, 0x38, 0x3c, 0x3f},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0xa0, 0xc4, 0x65, 0xee, 0x58, 0x96, 0x97, 0x2f, 0x83, 0x37, 0xaa, 0x1f, 0x92, 0xc9, 0x9d, 0x10},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x60, 0xdd, 0x95, 0x5e, 0xd0, 0xca, 0x3d, 0x7a, 0x64, 0x22, 0x71, 0x74, 0xdd, 0x98, 0xdd, 0x81},
padding.NewISO9797M2Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message for mac"),
[]byte{0x43, 0x05, 0x0d, 0x51, 0xc6, 0x56, 0xae, 0x60, 0xbe, 0x27, 0x3f, 0xbe, 0xa4, 0x87, 0x0e, 0xf1},
padding.NewISO9797M3Padding,
},
{
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10},
[]byte("This is the test message "),
[]byte{0x61, 0xe0, 0x00, 0x49, 0xe2, 0x69, 0x62, 0xa3, 0x6f, 0xed, 0xba, 0x8d, 0x4f, 0x52, 0xf0, 0xad},
padding.NewISO9797M3Padding,
},
}
for i, c := range cases {
mac := NewLMACWithPadding(sm4.NewCipher, c.key, 16, c.newPaddingFunc)
tag := mac.MAC(c.src)
if !bytes.Equal(tag, c.tag) {
t.Errorf("#%d: expect tag %x, got %x", i, c.tag, tag)
}
}
}
func TestTRCBCMAC(t *testing.T) { func TestTRCBCMAC(t *testing.T) {
// Test vectors from GB/T 15821.1-2020 Appendix B. // Test vectors from GB/T 15821.1-2020 Appendix B.
cases := []struct { cases := []struct {

View File

@ -8,6 +8,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/hex" "encoding/hex"
@ -22,14 +23,24 @@ func TestCreateCertificateRequest(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
certRSAKey, err := rsa.GenerateKey(random, 2048)
if err != nil {
t.Fatal(err)
}
tmpKey, err := sm2.GenerateKey(random) tmpKey, err := sm2.GenerateKey(random)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
invalidTmpKey, err := ecdsa.GenerateKey(elliptic.P256(), random) p256Key, err := ecdsa.GenerateKey(elliptic.P256(), random)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
rsaKey, err := rsa.GenerateKey(random, 2048)
if err != nil {
t.Fatal(err)
}
template := &x509.CertificateRequest{ template := &x509.CertificateRequest{
Subject: pkix.Name{ Subject: pkix.Name{
CommonName: "certRequisition", CommonName: "certRequisition",
@ -37,41 +48,110 @@ func TestCreateCertificateRequest(t *testing.T) {
Country: []string{"CN"}, Country: []string{"CN"},
}, },
} }
_, err = CreateCertificateRequest(random, template, "", "", "")
if err == nil || err.Error() != "x509: certificate private key does not implement crypto.Signer" { testCases := []struct {
t.Fatalf("expect certificate private key does not implement crypto.Signer, got %v", err) template *x509.CertificateRequest
priv interface{}
tmpPub interface{}
challengePassword string
wantErr bool
errormsg string
}{
{
template: template,
priv: certKey,
tmpPub: tmpKey.Public(),
challengePassword: "111111",
wantErr: false,
errormsg: "",
},
{
template: template,
priv: certRSAKey,
tmpPub: rsaKey.Public(),
challengePassword: "111111",
wantErr: false,
errormsg: "",
},
{
template: template,
priv: p256Key,
tmpPub: nil,
challengePassword: "",
wantErr: false,
errormsg: "",
},
{
template: template,
priv: "",
tmpPub: "",
challengePassword: "",
wantErr: true,
errormsg: "x509: certificate private key does not implement crypto.Signer",
},
{
template: template,
priv: certKey,
tmpPub: "",
challengePassword: "",
wantErr: true,
errormsg: "x509: SM2 temp public key is required",
},
{
template: template,
priv: certKey,
tmpPub: rsaKey.Public(),
challengePassword: "",
wantErr: true,
errormsg: "x509: SM2 temp public key is required",
},
{
template: template,
priv: certRSAKey,
tmpPub: tmpKey.Public(),
challengePassword: "",
wantErr: true,
errormsg: "x509: RSA temp public key is required",
},
{
template: template,
priv: certKey,
tmpPub: p256Key.Public(),
challengePassword: "",
wantErr: true,
errormsg: "x509: SM2 temp public key is required",
},
{
template: template,
priv: p256Key,
tmpPub: certKey.Public(),
challengePassword: "111111",
wantErr: true,
errormsg: "x509: only RSA or SM2 key is supported",
},
{
template: template,
priv: certKey,
tmpPub: tmpKey.Public(),
challengePassword: "",
wantErr: true,
errormsg: "x509: challenge password is required",
},
} }
_, err = CreateCertificateRequest(random, template, certKey, "", "") for _, tc := range testCases {
if err == nil || err.Error() != "x509: only SM2 public key is supported" { _, err := CreateCertificateRequest(random, tc.template, tc.priv, tc.tmpPub, tc.challengePassword)
t.Fatalf("expect only SM2 public key is supported, got %v", err) if tc.wantErr {
} if err == nil {
_, err = CreateCertificateRequest(random, template, certKey, invalidTmpKey.Public(), "") t.Fatal("expected error, got nil")
if err == nil || err.Error() != "x509: only SM2 public key is supported" { }
t.Fatalf("expect only SM2 public key is supported, got %v", err) if err.Error() != tc.errormsg {
} t.Fatalf("expected error %s, got %s", tc.errormsg, err.Error())
_, err = CreateCertificateRequest(random, template, certKey, tmpKey.Public(), "") }
if err == nil || err.Error() != "x509: challenge password is required" { } else {
t.Fatalf("expect challenge password is required, got %v", err) if err != nil {
} t.Fatal(err)
csrDer, err := CreateCertificateRequest(random, template, certKey, tmpKey.Public(), "111111") }
if err != nil { }
t.Fatal(err)
}
csr, err := ParseCertificateRequest(csrDer)
if err != nil {
t.Fatal(err)
}
if csr.Subject.CommonName != "certRequisition" {
t.Fatal("common name not match")
}
if csr.Subject.CommonName != "certRequisition" {
t.Fatal("common name not match")
}
if csr.ChallengePassword != "111111" {
t.Fatal("challenge password not match")
}
if !tmpKey.PublicKey.Equal(csr.TmpPublicKey) {
t.Fatal("tmp public key not match")
} }
} }

View File

@ -3,25 +3,23 @@
package cipher package cipher
import ( import (
_cipher "crypto/cipher" "bytes"
"crypto/cipher"
"github.com/emmansun/gmsm/internal/subtle" "crypto/subtle"
) )
type bc struct { type bc struct {
b _cipher.Block b cipher.Block
blockSize int blockSize int
iv []byte iv []byte
} }
func newBC(b _cipher.Block, iv []byte) *bc { func newBC(b cipher.Block, iv []byte) *bc {
c := &bc{ return &bc{
b: b, b: b,
blockSize: b.BlockSize(), blockSize: b.BlockSize(),
iv: make([]byte, b.BlockSize()), iv: bytes.Clone(iv),
} }
copy(c.iv, iv)
return c
} }
type bcEncrypter bc type bcEncrypter bc
@ -31,13 +29,13 @@ type bcEncrypter bc
// NewBCEncrypter will check for this interface and return the specific // NewBCEncrypter will check for this interface and return the specific
// BlockMode if found. // BlockMode if found.
type bcEncAble interface { type bcEncAble interface {
NewBCEncrypter(iv []byte) _cipher.BlockMode NewBCEncrypter(iv []byte) cipher.BlockMode
} }
// NewBCEncrypter returns a BlockMode which encrypts in block chaining // NewBCEncrypter returns a BlockMode which encrypts in block chaining
// mode, using the given Block. The length of iv must be the same as the // mode, using the given Block. The length of iv must be the same as the
// Block's block size. // Block's block size.
func NewBCEncrypter(b _cipher.Block, iv []byte) _cipher.BlockMode { func NewBCEncrypter(b cipher.Block, iv []byte) cipher.BlockMode {
if len(iv) != b.BlockSize() { if len(iv) != b.BlockSize() {
panic("cipher.NewBCEncrypter: IV length must equal block size") panic("cipher.NewBCEncrypter: IV length must equal block size")
} }
@ -82,13 +80,13 @@ type bcDecrypter bc
// NewBCDecrypter will check for this interface and return the specific // NewBCDecrypter will check for this interface and return the specific
// BlockMode if found. // BlockMode if found.
type bcDecAble interface { type bcDecAble interface {
NewBCDecrypter(iv []byte) _cipher.BlockMode NewBCDecrypter(iv []byte) cipher.BlockMode
} }
// NewBCDecrypter returns a BlockMode which decrypts in block chaining // NewBCDecrypter returns a BlockMode which decrypts in block chaining
// mode, using the given Block. The length of iv must be the same as the // mode, using the given Block. The length of iv must be the same as the
// Block's block size and must match the iv used to encrypt the data. // Block's block size and must match the iv used to encrypt the data.
func NewBCDecrypter(b _cipher.Block, iv []byte) _cipher.BlockMode { func NewBCDecrypter(b cipher.Block, iv []byte) cipher.BlockMode {
if len(iv) != b.BlockSize() { if len(iv) != b.BlockSize() {
panic("cipher.NewBCDecrypter: IV length must equal block size") panic("cipher.NewBCDecrypter: IV length must equal block size")
} }

View File

@ -2,15 +2,14 @@
package cipher package cipher
import ( import (
goCipher "crypto/cipher" "crypto/cipher"
goSubtle "crypto/subtle" "crypto/subtle"
"math" "math"
"errors" "errors"
"github.com/emmansun/gmsm/internal/alias" "github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/byteorder" "github.com/emmansun/gmsm/internal/byteorder"
"github.com/emmansun/gmsm/internal/subtle"
) )
const ( const (
@ -23,11 +22,11 @@ const (
// ccmAble is an interface implemented by ciphers that have a specific optimized // ccmAble is an interface implemented by ciphers that have a specific optimized
// implementation of CCM. // implementation of CCM.
type ccmAble interface { type ccmAble interface {
NewCCM(nonceSize, tagSize int) (goCipher.AEAD, error) NewCCM(nonceSize, tagSize int) (cipher.AEAD, error)
} }
type ccm struct { type ccm struct {
cipher goCipher.Block cipher cipher.Block
nonceSize int nonceSize int
tagSize int tagSize int
} }
@ -57,14 +56,14 @@ func maxlen(L, tagsize int) int {
// NewCCM returns the given 128-bit, block cipher wrapped in CCM // NewCCM returns the given 128-bit, block cipher wrapped in CCM
// with the standard nonce length. // with the standard nonce length.
func NewCCM(cipher goCipher.Block) (goCipher.AEAD, error) { func NewCCM(cipher cipher.Block) (cipher.AEAD, error) {
return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, ccmTagSize) return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, ccmTagSize)
} }
// NewCCMWithNonceSize returns the given 128-bit, block cipher wrapped in CCM, // NewCCMWithNonceSize returns the given 128-bit, block cipher wrapped in CCM,
// which accepts nonces of the given length. The length must not // which accepts nonces of the given length. The length must not
// be zero. // be zero.
func NewCCMWithNonceSize(cipher goCipher.Block, size int) (goCipher.AEAD, error) { func NewCCMWithNonceSize(cipher cipher.Block, size int) (cipher.AEAD, error) {
return NewCCMWithNonceAndTagSize(cipher, size, ccmTagSize) return NewCCMWithNonceAndTagSize(cipher, size, ccmTagSize)
} }
@ -72,12 +71,12 @@ func NewCCMWithNonceSize(cipher goCipher.Block, size int) (goCipher.AEAD, error)
// which generates tags with the given length. // which generates tags with the given length.
// //
// Tag sizes between 8 and 16 bytes are allowed. // Tag sizes between 8 and 16 bytes are allowed.
func NewCCMWithTagSize(cipher goCipher.Block, tagSize int) (goCipher.AEAD, error) { func NewCCMWithTagSize(cipher cipher.Block, tagSize int) (cipher.AEAD, error) {
return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, tagSize) return NewCCMWithNonceAndTagSize(cipher, ccmStandardNonceSize, tagSize)
} }
// https://tools.ietf.org/html/rfc3610 // https://tools.ietf.org/html/rfc3610
func NewCCMWithNonceAndTagSize(cipher goCipher.Block, nonceSize, tagSize int) (goCipher.AEAD, error) { func NewCCMWithNonceAndTagSize(cipher cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
if tagSize < ccmMinimumTagSize || tagSize > ccmBlockSize || tagSize&1 != 0 { if tagSize < ccmMinimumTagSize || tagSize > ccmBlockSize || tagSize&1 != 0 {
return nil, errors.New("cipher: incorrect tag size given to CCM") return nil, errors.New("cipher: incorrect tag size given to CCM")
} }
@ -189,7 +188,7 @@ func (c *ccm) Seal(dst, nonce, plaintext, data []byte) []byte {
c.cipher.Encrypt(tagMask[:], counter[:]) c.cipher.Encrypt(tagMask[:], counter[:])
counter[len(counter)-1] |= 1 counter[len(counter)-1] |= 1
ctr := goCipher.NewCTR(c.cipher, counter[:]) ctr := cipher.NewCTR(c.cipher, counter[:])
ctr.XORKeyStream(out, plaintext) ctr.XORKeyStream(out, plaintext)
tag := c.auth(nonce, plaintext, data, &tagMask) tag := c.auth(nonce, plaintext, data, &tagMask)
@ -231,17 +230,15 @@ func (c *ccm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
} }
counter[len(counter)-1] |= 1 counter[len(counter)-1] |= 1
ctr := goCipher.NewCTR(c.cipher, counter[:]) ctr := cipher.NewCTR(c.cipher, counter[:])
ctr.XORKeyStream(out, ciphertext) ctr.XORKeyStream(out, ciphertext)
expectedTag := c.auth(nonce, out, data, &tagMask) expectedTag := c.auth(nonce, out, data, &tagMask)
if goSubtle.ConstantTimeCompare(expectedTag, tag) != 1 { if subtle.ConstantTimeCompare(expectedTag, tag) != 1 {
// The AESNI code decrypts and authenticates concurrently, and // The AESNI code decrypts and authenticates concurrently, and
// so overwrites dst in the event of a tag mismatch. That // so overwrites dst in the event of a tag mismatch. That
// behavior is mimicked here in order to be consistent across // behavior is mimicked here in order to be consistent across
// platforms. // platforms.
for i := range out { clear(out)
out[i] = 0
}
return nil, errOpen return nil, errOpen
} }
return ret, nil return ret, nil

14
cipher/common.go Normal file
View File

@ -0,0 +1,14 @@
package cipher
import "crypto/cipher"
// blockSize is the block size that the underlying cipher must have.
const blockSize = 16
type concurrentBlocks interface {
Concurrency() int
EncryptBlocks(dst, src []byte)
DecryptBlocks(dst, src []byte)
}
type CipherCreator func([]byte) (cipher.Block, error)

View File

@ -1,12 +1,12 @@
package cipher package cipher
import ( import (
_cipher "crypto/cipher" "crypto/cipher"
"crypto/subtle"
"errors" "errors"
"github.com/emmansun/gmsm/internal/alias" "github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/byteorder" "github.com/emmansun/gmsm/internal/byteorder"
"github.com/emmansun/gmsm/internal/subtle"
) )
// A LengthPreservingMode represents a block cipher running in a length preserving mode (HCTR, // A LengthPreservingMode represents a block cipher running in a length preserving mode (HCTR,
@ -105,7 +105,7 @@ var hctrReductionTable = []uint16{
// https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288 // https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.470.5288
// GB/T 17964-2021 第11章 带泛杂凑函数的计数器工作模式 // GB/T 17964-2021 第11章 带泛杂凑函数的计数器工作模式
type hctr struct { type hctr struct {
cipher _cipher.Block cipher cipher.Block
tweak [blockSize]byte tweak [blockSize]byte
// productTable contains the first sixteen powers of the hash key. // productTable contains the first sixteen powers of the hash key.
// However, they are in bit reversed order. // However, they are in bit reversed order.
@ -118,7 +118,7 @@ func (h *hctr) BlockSize() int {
// NewHCTR returns a [LengthPreservingMode] which encrypts/decrypts useing the given [Block] // 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. // 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) { func NewHCTR(cipher cipher.Block, tweak, hkey []byte) (LengthPreservingMode, error) {
if len(tweak) != blockSize || len(hkey) != blockSize { if len(tweak) != blockSize || len(hkey) != blockSize {
return nil, errors.New("cipher: invalid tweak and/or hash key length") return nil, errors.New("cipher: invalid tweak and/or hash key length")
} }

View File

@ -4,6 +4,7 @@
package cipher package cipher
import ( import (
"bytes"
_cipher "crypto/cipher" _cipher "crypto/cipher"
"errors" "errors"
) )
@ -28,8 +29,8 @@ func newOFBNLF(cipherFunc CipherCreator, key, iv []byte) (*ofbnlf, error) {
if len(iv) != c.blockSize { if len(iv) != c.blockSize {
return nil, errors.New("cipher: IV length must equal block size") return nil, errors.New("cipher: IV length must equal block size")
} }
c.iv = make([]byte, c.blockSize) c.iv = bytes.Clone(iv)
copy(c.iv, iv)
return c, nil return c, nil
} }

View File

@ -1,54 +1,21 @@
package cipher package cipher
import ( import (
_cipher "crypto/cipher" "crypto/cipher"
"errors"
"github.com/emmansun/gmsm/internal/alias"
"github.com/emmansun/gmsm/internal/byteorder" "github.com/emmansun/gmsm/internal/byteorder"
"github.com/emmansun/gmsm/internal/subtle" "github.com/emmansun/gmsm/internal/cipher/xts"
) )
const GF128_FDBK byte = 0x87
type CipherCreator func([]byte) (_cipher.Block, error)
type concurrentBlocks interface {
Concurrency() int
EncryptBlocks(dst, src []byte)
DecryptBlocks(dst, src []byte)
}
// Cipher contains an expanded key structure. It is unsafe for concurrent use.
type xts struct {
b _cipher.Block
tweak [blockSize]byte
isGB bool // if true, follows GB/T 17964-2021
}
// blockSize is the block size that the underlying cipher must have. XTS is
// only defined for 16-byte ciphers.
const blockSize = 16
type xtsEncrypter xts
// xtsEncAble is an interface implemented by ciphers that have a specific
// optimized implementation of XTS encryption, like sm4.
// NewXTSEncrypter will check for this interface and return the specific
// BlockMode if found.
type xtsEncAble interface {
NewXTSEncrypter(encryptedTweak *[blockSize]byte, isGB bool) _cipher.BlockMode
}
// NewXTSEncrypter creates a Cipher given a function for creating the underlying // NewXTSEncrypter creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes). // block cipher (which must have a block size of 16 bytes).
func NewXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) { func NewXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
return newXTSEncrypter(cipherFunc, key, tweakKey, tweak, false) return xts.NewXTSEncrypter(cipherFunc, key, tweakKey, tweak, false)
} }
// NewXTSEncrypterWithSector creates a Cipher given a function for creating the underlying // NewXTSEncrypterWithSector creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) with sector number. // block cipher (which must have a block size of 16 bytes) with sector number.
func NewXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) { func NewXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
tweak := make([]byte, blockSize) tweak := make([]byte, blockSize)
byteorder.LEPutUint64(tweak[:8], sectorNum) byteorder.LEPutUint64(tweak[:8], sectorNum)
return NewXTSEncrypter(cipherFunc, key, tweakKey, tweak) return NewXTSEncrypter(cipherFunc, key, tweakKey, tweak)
@ -57,70 +24,28 @@ func NewXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, s
// NewGBXTSEncrypter creates a Cipher given a function for creating the underlying // NewGBXTSEncrypter creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes). // block cipher (which must have a block size of 16 bytes).
// It follows GB/T 17964-2021. // It follows GB/T 17964-2021.
func NewGBXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) { func NewGBXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
return newXTSEncrypter(cipherFunc, key, tweakKey, tweak, true) return xts.NewXTSEncrypter(cipherFunc, key, tweakKey, tweak, true)
} }
// NewGBXTSEncrypterWithSector creates a Cipher given a function for creating the underlying // NewGBXTSEncrypterWithSector creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) with sector number. // block cipher (which must have a block size of 16 bytes) with sector number.
// It follows GB/T 17964-2021. // It follows GB/T 17964-2021.
func NewGBXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) { func NewGBXTSEncrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
tweak := make([]byte, blockSize) tweak := make([]byte, blockSize)
byteorder.LEPutUint64(tweak[:8], sectorNum) byteorder.LEPutUint64(tweak[:8], sectorNum)
return NewGBXTSEncrypter(cipherFunc, key, tweakKey, tweak) return NewGBXTSEncrypter(cipherFunc, key, tweakKey, tweak)
} }
func newXTSEncrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte, isGB bool) (_cipher.BlockMode, error) {
if len(tweak) != blockSize {
return nil, errors.New("cipher: invalid tweak length")
}
k1, err := cipherFunc(key)
if err != nil {
return nil, err
}
if k1.BlockSize() != blockSize {
return nil, errors.New("cipher: cipher does not have a block size of 16")
}
k2, err := cipherFunc(tweakKey)
if err != nil {
return nil, err
}
if xtsable, ok := k1.(xtsEncAble); ok {
var encryptedTweak [blockSize]byte
k2.Encrypt(encryptedTweak[:], tweak)
return xtsable.NewXTSEncrypter(&encryptedTweak, isGB), nil
}
c := &xts{
b: k1,
isGB: isGB,
}
k2.Encrypt(c.tweak[:], tweak)
return (*xtsEncrypter)(c), nil
}
type xtsDecrypter xts
// xtsDecAble is an interface implemented by ciphers that have a specific
// optimized implementation of XTS encryption, like sm4.
// NewXTSDecrypter will check for this interface and return the specific
// BlockMode if found.
type xtsDecAble interface {
NewXTSDecrypter(encryptedTweak *[blockSize]byte, isGB bool) _cipher.BlockMode
}
// NewXTSDecrypter creates a Cipher given a function for creating the underlying // NewXTSDecrypter creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) for decryption. // block cipher (which must have a block size of 16 bytes) for decryption.
func NewXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) { func NewXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
return newXTSDecrypter(cipherFunc, key, tweakKey, tweak, false) return xts.NewXTSDecrypter(cipherFunc, key, tweakKey, tweak, false)
} }
// NewXTSDecrypterWithSector creates a Cipher given a function for creating the underlying // NewXTSDecrypterWithSector creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) with sector number for decryption. // block cipher (which must have a block size of 16 bytes) with sector number for decryption.
func NewXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) { func NewXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
tweak := make([]byte, blockSize) tweak := make([]byte, blockSize)
byteorder.LEPutUint64(tweak[:8], sectorNum) byteorder.LEPutUint64(tweak[:8], sectorNum)
return NewXTSDecrypter(cipherFunc, key, tweakKey, tweak) return NewXTSDecrypter(cipherFunc, key, tweakKey, tweak)
@ -129,225 +54,15 @@ func NewXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, s
// NewGBXTSDecrypter creates a Cipher given a function for creating the underlying // NewGBXTSDecrypter creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) for decryption. // block cipher (which must have a block size of 16 bytes) for decryption.
// It follows GB/T 17964-2021. // It follows GB/T 17964-2021.
func NewGBXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (_cipher.BlockMode, error) { func NewGBXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte) (cipher.BlockMode, error) {
return newXTSDecrypter(cipherFunc, key, tweakKey, tweak, true) return xts.NewXTSDecrypter(cipherFunc, key, tweakKey, tweak, true)
} }
// NewGBXTSDecrypterWithSector creates a Cipher given a function for creating the underlying // NewGBXTSDecrypterWithSector creates a Cipher given a function for creating the underlying
// block cipher (which must have a block size of 16 bytes) with sector number for decryption. // block cipher (which must have a block size of 16 bytes) with sector number for decryption.
// It follows GB/T 17964-2021. // It follows GB/T 17964-2021.
func NewGBXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (_cipher.BlockMode, error) { func NewGBXTSDecrypterWithSector(cipherFunc CipherCreator, key, tweakKey []byte, sectorNum uint64) (cipher.BlockMode, error) {
tweak := make([]byte, blockSize) tweak := make([]byte, blockSize)
byteorder.LEPutUint64(tweak[:8], sectorNum) byteorder.LEPutUint64(tweak[:8], sectorNum)
return NewGBXTSDecrypter(cipherFunc, key, tweakKey, tweak) return NewGBXTSDecrypter(cipherFunc, key, tweakKey, tweak)
} }
func newXTSDecrypter(cipherFunc CipherCreator, key, tweakKey, tweak []byte, isGB bool) (_cipher.BlockMode, error) {
if len(tweak) != blockSize {
return nil, errors.New("cipher: invalid tweak length")
}
k1, err := cipherFunc(key)
if err != nil {
return nil, err
}
if k1.BlockSize() != blockSize {
return nil, errors.New("cipher: cipher does not have a block size of 16")
}
k2, err := cipherFunc(tweakKey)
if err != nil {
return nil, err
}
if xtsable, ok := k1.(xtsDecAble); ok {
var encryptedTweak [blockSize]byte
k2.Encrypt(encryptedTweak[:], tweak)
return xtsable.NewXTSDecrypter(&encryptedTweak, isGB), nil
}
c := &xts{
b: k1,
isGB: isGB,
}
k2.Encrypt(c.tweak[:], tweak)
return (*xtsDecrypter)(c), nil
}
func (c *xtsEncrypter) BlockSize() int {
return blockSize
}
// CryptBlocks encrypts a sector of plaintext and puts the result into ciphertext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *xtsEncrypter) CryptBlocks(ciphertext, plaintext []byte) {
if len(ciphertext) < len(plaintext) {
panic("cipher: ciphertext is smaller than plaintext")
}
if len(plaintext) < blockSize {
panic("cipher: plaintext length is smaller than the block size")
}
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
panic("cipher: invalid buffer overlap")
}
lastCiphertext := ciphertext
if concCipher, ok := c.b.(concurrentBlocks); ok {
batchSize := concCipher.Concurrency() * blockSize
var tweaks []byte = make([]byte, batchSize)
for len(plaintext) >= batchSize {
doubleTweaks(&c.tweak, tweaks, c.isGB)
subtle.XORBytes(ciphertext, plaintext, tweaks)
concCipher.EncryptBlocks(ciphertext, ciphertext)
subtle.XORBytes(ciphertext, ciphertext, tweaks)
plaintext = plaintext[batchSize:]
lastCiphertext = ciphertext[batchSize-blockSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(plaintext) >= blockSize {
subtle.XORBytes(ciphertext, plaintext, c.tweak[:])
c.b.Encrypt(ciphertext, ciphertext)
subtle.XORBytes(ciphertext, ciphertext, c.tweak[:])
plaintext = plaintext[blockSize:]
lastCiphertext = ciphertext
ciphertext = ciphertext[blockSize:]
mul2(&c.tweak, c.isGB)
}
// is there a final partial block to handle?
if remain := len(plaintext); remain > 0 {
var x [blockSize]byte
//Copy the final plaintext bytes
copy(x[:], plaintext)
//Steal ciphertext to complete the block
copy(x[remain:], lastCiphertext[remain:blockSize])
//Copy the final ciphertext bytes
copy(ciphertext, lastCiphertext[:remain])
//Merge the tweak into the input block
subtle.XORBytes(x[:], x[:], c.tweak[:])
//Encrypt the final block using K1
c.b.Encrypt(x[:], x[:])
//Merge the tweak into the output block
subtle.XORBytes(lastCiphertext, x[:], c.tweak[:])
}
}
func (c *xtsDecrypter) BlockSize() int {
return blockSize
}
// CryptBlocks decrypts a sector of ciphertext and puts the result into plaintext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *xtsDecrypter) CryptBlocks(plaintext, ciphertext []byte) {
if len(plaintext) < len(ciphertext) {
panic("cipher: plaintext is smaller than ciphertext")
}
if len(ciphertext) < blockSize {
panic("cipher: ciphertext length is smaller than the block size")
}
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
panic("cipher: invalid buffer overlap")
}
if concCipher, ok := c.b.(concurrentBlocks); ok {
batchSize := concCipher.Concurrency() * blockSize
var tweaks []byte = make([]byte, batchSize)
for len(ciphertext) >= batchSize {
doubleTweaks(&c.tweak, tweaks, c.isGB)
subtle.XORBytes(plaintext, ciphertext, tweaks)
concCipher.DecryptBlocks(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, tweaks)
plaintext = plaintext[batchSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(ciphertext) >= 2*blockSize {
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
c.b.Decrypt(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
plaintext = plaintext[blockSize:]
ciphertext = ciphertext[blockSize:]
mul2(&c.tweak, c.isGB)
}
if remain := len(ciphertext); remain >= blockSize {
var x [blockSize]byte
if remain > blockSize {
var tt [blockSize]byte
copy(tt[:], c.tweak[:])
mul2(&tt, c.isGB)
subtle.XORBytes(x[:], ciphertext, tt[:])
c.b.Decrypt(x[:], x[:])
subtle.XORBytes(plaintext, x[:], tt[:])
//Retrieve the length of the final block
remain -= blockSize
//Copy the final ciphertext bytes
copy(x[:], ciphertext[blockSize:])
//Steal ciphertext to complete the block
copy(x[remain:], plaintext[remain:blockSize])
//Copy the final plaintext bytes
copy(plaintext[blockSize:], plaintext)
subtle.XORBytes(x[:], x[:], c.tweak[:])
c.b.Decrypt(x[:], x[:])
subtle.XORBytes(plaintext, x[:], c.tweak[:])
} else {
//The last block contains exactly 128 bits
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
c.b.Decrypt(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
// Maybe there are still ciphertext
mul2(&c.tweak, c.isGB)
}
}
}
// mul2Generic multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
// x¹²⁸ + x⁷ + x² + x + 1.
func mul2Generic(tweak *[blockSize]byte, isGB bool) {
var carryIn byte
if !isGB {
// the coefficient of x⁰ can be obtained by tweak[0] & 1
// the coefficient of x⁷ can be obtained by tweak[0] >> 7
// the coefficient of x¹²⁰ can be obtained by tweak[15] & 1
// the coefficient of x¹²⁷ can be obtained by tweak[15] >> 7
for j := range tweak {
carryOut := tweak[j] >> 7
tweak[j] = (tweak[j] << 1) + carryIn
carryIn = carryOut
}
if carryIn != 0 {
// If we have a carry bit then we need to subtract a multiple
// of the irreducible polynomial (x¹²⁸ + x⁷ + x² + x + 1).
// By dropping the carry bit, we're subtracting the x^128 term
// so all that remains is to subtract x⁷ + x² + x + 1.
// Subtraction (and addition) in this representation is just
// XOR.
tweak[0] ^= GF128_FDBK // 1<<7 | 1<<2 | 1<<1 | 1
}
} else {
// GB/T 17964-2021,
// the coefficient of x⁰ can be obtained by tweak[0] >> 7
// the coefficient of x⁷ can be obtained by tweak[0] & 1
// the coefficient of x¹²⁰ can be obtained by tweak[15] >> 7
// the coefficient of x¹²⁷ can be obtained by tweak[15] & 1
for j := range tweak {
carryOut := (tweak[j] << 7) & 0x80
tweak[j] = (tweak[j] >> 1) + carryIn
carryIn = carryOut
}
if carryIn != 0 {
tweak[0] ^= 0xE1 // 1<<7 | 1<<6 | 1<<5 | 1
}
}
}

View File

@ -104,7 +104,8 @@ SADK 3.2之后的版本支持下列SM2密文格式(encryptedType)
* 0x02/0x03 - C1为压缩点格式具体是C1C3C2还是C1C2C3取决于解密时的选项参数默认为C1C3C2。 * 0x02/0x03 - C1为压缩点格式具体是C1C3C2还是C1C2C3取决于解密时的选项参数默认为C1C3C2。
### 生成双密钥CSR v0.29.6+ ### 生成双密钥CSR v0.29.6+
`cfca.CreateCertificateRequest`和CFCA SADK不同调用者需要自行先生成两对密钥对一对用于签名证书一对用于加解密CFCA生成的加密用私钥文件CFCA加密申请者解密 `cfca.CreateCertificateRequest`和CFCA SADK不同调用者需要自行先生成两对密钥对一对用于签名证书一对用于加解密CFCA生成的加密用私钥文件CFCA加密申请者解密。这个方法对应CFCA的`cfca.sadk.util.P10Request.generateDoublePKCS10Request`方法。按我的理解非国密RSA应该不需要支持这种双密钥对机制不过既然**CFCA SADK**支持,本软件库从**v0.30.0**开始也支持。
使用`cfca.ParseEscrowPrivateKey`解析CFCA返回的加密用私钥。 使用`cfca.ParseEscrowPrivateKey`解析CFCA返回的加密用私钥。
### SM2私钥、证书的解析 ### SM2私钥、证书的解析

View File

@ -99,6 +99,10 @@ func ExampleNewCipher_zuc256() {
// Output: some plaintext // Output: some plaintext
} }
``` ```
### Seekable Stream
完整性算法支持Seekable Stream也就是随机定位到某点进行处理内部实现了分桶缓存状态每个状态的大小大概是88字节`bucketSize`的大小可以结合要处理的流大小以及内存占用来平衡考虑。同时,`bucketSize`内部会被处理成128字节的倍数以利于实现。
如果您没有对同一个流反复进行**前进**、**后退**加解密的需求,可以使用`NewCipher`或者`NewEEACipher`方法,避免内部状态缓存。
## 完整性算法 ## 完整性算法
完整性算法实现了```hash.Hash```接口,所以其使用方法和其它哈希算法类似。 完整性算法实现了```hash.Hash```接口,所以其使用方法和其它哈希算法类似。

View File

@ -2,11 +2,11 @@ package drbg
import ( import (
"crypto/cipher" "crypto/cipher"
"crypto/subtle"
"errors" "errors"
"time" "time"
"github.com/emmansun/gmsm/internal/byteorder" "github.com/emmansun/gmsm/internal/byteorder"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/sm4" "github.com/emmansun/gmsm/sm4"
) )
@ -120,12 +120,12 @@ func (hd *CtrDrbg) MaxBytesPerRequest() int {
} }
// Generate CTR DRBG pseudorandom bits generate process. // Generate CTR DRBG pseudorandom bits generate process.
func (hd *CtrDrbg) Generate(b, additional []byte) error { func (hd *CtrDrbg) Generate(out, additional []byte) error {
if hd.NeedReseed() { if hd.NeedReseed() {
return ErrReseedRequired return ErrReseedRequired
} }
outlen := len(hd.v) outlen := len(hd.v)
if (hd.gm && len(b) > outlen) || (!hd.gm && len(b) > MAX_BYTES_PER_GENERATE) { if (hd.gm && len(out) > outlen) || (!hd.gm && len(out) > MAX_BYTES_PER_GENERATE) {
return errors.New("drbg: too many bytes requested") return errors.New("drbg: too many bytes requested")
} }
@ -140,14 +140,14 @@ func (hd *CtrDrbg) Generate(b, additional []byte) error {
block := hd.newBlockCipher(hd.key) block := hd.newBlockCipher(hd.key)
temp := make([]byte, outlen) temp := make([]byte, outlen)
m := len(b) m := len(out)
limit := uint64(m+outlen-1) / uint64(outlen) limit := uint64(m+outlen-1) / uint64(outlen)
for i := 0; i < int(limit); i++ { for i := range int(limit) {
// V = (V + 1) mod 2^outlen) // V = (V + 1) mod 2^outlen)
addOne(hd.v, outlen) addOne(hd.v, outlen)
// output_block = Encrypt(Key, V) // output_block = Encrypt(Key, V)
block.Encrypt(temp, hd.v) block.Encrypt(temp, hd.v)
copy(b[i*outlen:], temp) copy(out[i*outlen:], temp)
} }
hd.update(additional) hd.update(additional)
hd.reseedCounter++ hd.reseedCounter++
@ -162,7 +162,7 @@ func (cd *CtrDrbg) update(seedMaterial []byte) {
v := make([]byte, outlen) v := make([]byte, outlen)
output := make([]byte, outlen) output := make([]byte, outlen)
copy(v, cd.v) copy(v, cd.v)
for i := 0; i < (cd.seedLength+outlen-1)/outlen; i++ { for i := range (cd.seedLength+outlen-1)/outlen {
// V = (V + 1) mod 2^outlen // V = (V + 1) mod 2^outlen
addOne(v, outlen) addOne(v, outlen)
// output_block = Encrypt(Key, V) // output_block = Encrypt(Key, V)
@ -191,7 +191,7 @@ func (cd *CtrDrbg) derive(seedMaterial []byte, returnBytes int) []byte {
S[outlen+8+len(seedMaterial)] = 0x80 S[outlen+8+len(seedMaterial)] = 0x80
key := make([]byte, cd.keyLen) key := make([]byte, cd.keyLen)
for i := 0; i < cd.keyLen; i++ { for i := range cd.keyLen {
key[i] = byte(i) key[i] = byte(i)
} }
blocks := (cd.seedLength + outlen - 1) / outlen blocks := (cd.seedLength + outlen - 1) / outlen

View File

@ -187,7 +187,7 @@ func (hd *HashDrbg) Generate(b, additional []byte) error {
limit := uint64(m+md.Size()-1) / uint64(md.Size()) limit := uint64(m+md.Size()-1) / uint64(md.Size())
data := make([]byte, hd.seedLength) data := make([]byte, hd.seedLength)
copy(data, hd.v) copy(data, hd.v)
for i := 0; i < int(limit); i++ { for i := range int(limit) {
md.Write(data) md.Write(data)
copy(b[i*md.Size():], md.Sum(nil)) copy(b[i*md.Size():], md.Sum(nil))
addOne(data, hd.seedLength) addOne(data, hd.seedLength)
@ -211,7 +211,7 @@ func (hd *HashDrbg) derive(seedMaterial []byte, len int) []byte {
byteorder.BEPutUint32(requireBytes[:], uint32(len<<3)) byteorder.BEPutUint32(requireBytes[:], uint32(len<<3))
var ct byte = 1 var ct byte = 1
k := make([]byte, len) k := make([]byte, len)
for i := 0; i < int(limit); i++ { for i := range int(limit) {
// Hash( counter_byte || return_bits || seed_material ) // Hash( counter_byte || return_bits || seed_material )
md.Write([]byte{ct}) md.Write([]byte{ct})
md.Write(requireBytes[:]) md.Write(requireBytes[:])

View File

@ -45,7 +45,7 @@ func NewHmacDrbg(newHash func() hash.Hash, securityLevel SecurityLevel, gm bool,
// HMAC_DRBG_Instantiate_process // HMAC_DRBG_Instantiate_process
hd.key = make([]byte, hd.hashSize) hd.key = make([]byte, hd.hashSize)
hd.v = make([]byte, hd.hashSize) hd.v = make([]byte, hd.hashSize)
for i := 0; i < hd.hashSize; i++ { for i := range hd.hashSize {
hd.key[i] = 0x00 hd.key[i] = 0x00
hd.v[i] = 0x01 hd.v[i] = 0x01
} }

View File

@ -374,9 +374,7 @@ type zr struct{}
// Read replaces the contents of dst with zeros. It is safe for concurrent use. // Read replaces the contents of dst with zeros. It is safe for concurrent use.
func (zr) Read(dst []byte) (n int, err error) { func (zr) Read(dst []byte) (n int, err error) {
for i := range dst { clear(dst)
dst[i] = 0
}
return len(dst), nil return len(dst), nil
} }

9
go.mod
View File

@ -1,8 +1,7 @@
module github.com/emmansun/gmsm module github.com/emmansun/gmsm
go 1.18 go 1.23.0
require ( require golang.org/x/crypto v0.37.0
golang.org/x/crypto v0.33.0
golang.org/x/sys v0.30.0 require golang.org/x/sys v0.32.0 // indirect
)

8
go.sum
View File

@ -1,4 +1,4 @@
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=

View File

@ -85,10 +85,7 @@ func (x *Nat) expand(n int) *Nat {
return x return x
} }
extraLimbs := x.limbs[len(x.limbs):n] extraLimbs := x.limbs[len(x.limbs):n]
// clear(extraLimbs) clear(extraLimbs)
for i := range extraLimbs {
extraLimbs[i] = 0
}
x.limbs = x.limbs[:n] x.limbs = x.limbs[:n]
return x return x
} }
@ -99,10 +96,8 @@ func (x *Nat) reset(n int) *Nat {
x.limbs = make([]uint, n) x.limbs = make([]uint, n)
return x return x
} }
// clear(x.limbs) // Clear both the returned limbs and the previously used ones.
for i := range x.limbs { clear(x.limbs[:max(n, len(x.limbs))])
x.limbs[i] = 0
}
x.limbs = x.limbs[:n] x.limbs = x.limbs[:n]
return x return x
} }

View File

@ -6,7 +6,7 @@
package bigmod package bigmod
import "golang.org/x/sys/cpu" import "github.com/emmansun/gmsm/internal/deps/cpu"
// amd64 assembly uses ADCX/ADOX/MULX if ADX is available to run two carry // amd64 assembly uses ADCX/ADOX/MULX if ADX is available to run two carry
// chains in the flags in parallel across the whole operation, and aggressively // chains in the flags in parallel across the whole operation, and aggressively

292
internal/cipher/xts/xts.go Normal file
View File

@ -0,0 +1,292 @@
// Package xts implements XTS encryption mode, as specified in IEEE P1619/D16 and GB/T 17964-2021.
package xts
import (
"crypto/cipher"
"crypto/subtle"
"errors"
"github.com/emmansun/gmsm/internal/alias"
)
const GF128_FDBK byte = 0x87
type concurrentBlocks interface {
Concurrency() int
EncryptBlocks(dst, src []byte)
DecryptBlocks(dst, src []byte)
}
// Cipher contains an expanded key structure. It is unsafe for concurrent use.
type xts struct {
b cipher.Block
tweak [blockSize]byte
isGB bool // if true, follows GB/T 17964-2021
}
// blockSize is the block size that the underlying cipher must have. XTS is
// only defined for 16-byte ciphers.
const blockSize = 16
type xtsEncrypter xts
// xtsEncAble is an interface implemented by ciphers that have a specific
// optimized implementation of XTS encryption, like sm4.
// NewXTSEncrypter will check for this interface and return the specific
// BlockMode if found.
type xtsEncAble interface {
NewXTSEncrypter(encryptedTweak *[blockSize]byte, isGB bool) cipher.BlockMode
}
func NewXTSEncrypter(cipherFunc func([]byte) (cipher.Block, error), key, tweakKey, tweak []byte, isGB bool) (cipher.BlockMode, error) {
if len(tweak) != blockSize {
return nil, errors.New("cipher: invalid tweak length")
}
k1, err := cipherFunc(key)
if err != nil {
return nil, err
}
if k1.BlockSize() != blockSize {
return nil, errors.New("cipher: cipher does not have a block size of 16")
}
k2, err := cipherFunc(tweakKey)
if err != nil {
return nil, err
}
if xtsable, ok := k1.(xtsEncAble); ok {
var encryptedTweak [blockSize]byte
k2.Encrypt(encryptedTweak[:], tweak)
return xtsable.NewXTSEncrypter(&encryptedTweak, isGB), nil
}
c := &xts{
b: k1,
isGB: isGB,
}
k2.Encrypt(c.tweak[:], tweak)
return (*xtsEncrypter)(c), nil
}
type xtsDecrypter xts
// xtsDecAble is an interface implemented by ciphers that have a specific
// optimized implementation of XTS encryption, like sm4.
// NewXTSDecrypter will check for this interface and return the specific
// BlockMode if found.
type xtsDecAble interface {
NewXTSDecrypter(encryptedTweak *[blockSize]byte, isGB bool) cipher.BlockMode
}
func NewXTSDecrypter(cipherFunc func([]byte) (cipher.Block, error), key, tweakKey, tweak []byte, isGB bool) (cipher.BlockMode, error) {
if len(tweak) != blockSize {
return nil, errors.New("cipher: invalid tweak length")
}
k1, err := cipherFunc(key)
if err != nil {
return nil, err
}
if k1.BlockSize() != blockSize {
return nil, errors.New("cipher: cipher does not have a block size of 16")
}
k2, err := cipherFunc(tweakKey)
if err != nil {
return nil, err
}
if xtsable, ok := k1.(xtsDecAble); ok {
var encryptedTweak [blockSize]byte
k2.Encrypt(encryptedTweak[:], tweak)
return xtsable.NewXTSDecrypter(&encryptedTweak, isGB), nil
}
c := &xts{
b: k1,
isGB: isGB,
}
k2.Encrypt(c.tweak[:], tweak)
return (*xtsDecrypter)(c), nil
}
func (c *xtsEncrypter) BlockSize() int {
return blockSize
}
// CryptBlocks encrypts a sector of plaintext and puts the result into ciphertext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *xtsEncrypter) CryptBlocks(ciphertext, plaintext []byte) {
if len(ciphertext) < len(plaintext) {
panic("cipher: ciphertext is smaller than plaintext")
}
if len(plaintext) < blockSize {
panic("cipher: plaintext length is smaller than the block size")
}
if alias.InexactOverlap(ciphertext[:len(plaintext)], plaintext) {
panic("cipher: invalid buffer overlap")
}
lastCiphertext := ciphertext
if concCipher, ok := c.b.(concurrentBlocks); ok {
batchSize := concCipher.Concurrency() * blockSize
var tweaks []byte = make([]byte, batchSize)
for len(plaintext) >= batchSize {
doubleTweaks(&c.tweak, tweaks, c.isGB)
subtle.XORBytes(ciphertext, plaintext, tweaks)
concCipher.EncryptBlocks(ciphertext, ciphertext)
subtle.XORBytes(ciphertext, ciphertext, tweaks)
plaintext = plaintext[batchSize:]
lastCiphertext = ciphertext[batchSize-blockSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(plaintext) >= blockSize {
subtle.XORBytes(ciphertext, plaintext, c.tweak[:])
c.b.Encrypt(ciphertext, ciphertext)
subtle.XORBytes(ciphertext, ciphertext, c.tweak[:])
plaintext = plaintext[blockSize:]
lastCiphertext = ciphertext
ciphertext = ciphertext[blockSize:]
mul2(&c.tweak, c.isGB)
}
// is there a final partial block to handle?
if remain := len(plaintext); remain > 0 {
var x [blockSize]byte
//Copy the final plaintext bytes
copy(x[:], plaintext)
//Steal ciphertext to complete the block
copy(x[remain:], lastCiphertext[remain:blockSize])
//Copy the final ciphertext bytes
copy(ciphertext, lastCiphertext[:remain])
//Merge the tweak into the input block
subtle.XORBytes(x[:], x[:], c.tweak[:])
//Encrypt the final block using K1
c.b.Encrypt(x[:], x[:])
//Merge the tweak into the output block
subtle.XORBytes(lastCiphertext, x[:], c.tweak[:])
}
}
func (c *xtsDecrypter) BlockSize() int {
return blockSize
}
// CryptBlocks decrypts a sector of ciphertext and puts the result into plaintext.
// Plaintext and ciphertext must overlap entirely or not at all.
// Sectors must be a multiple of 16 bytes and less than 2²⁴ bytes.
func (c *xtsDecrypter) CryptBlocks(plaintext, ciphertext []byte) {
if len(plaintext) < len(ciphertext) {
panic("cipher: plaintext is smaller than ciphertext")
}
if len(ciphertext) < blockSize {
panic("cipher: ciphertext length is smaller than the block size")
}
if alias.InexactOverlap(plaintext[:len(ciphertext)], ciphertext) {
panic("cipher: invalid buffer overlap")
}
if concCipher, ok := c.b.(concurrentBlocks); ok {
batchSize := concCipher.Concurrency() * blockSize
var tweaks []byte = make([]byte, batchSize)
for len(ciphertext) >= batchSize {
doubleTweaks(&c.tweak, tweaks, c.isGB)
subtle.XORBytes(plaintext, ciphertext, tweaks)
concCipher.DecryptBlocks(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, tweaks)
plaintext = plaintext[batchSize:]
ciphertext = ciphertext[batchSize:]
}
}
for len(ciphertext) >= 2*blockSize {
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
c.b.Decrypt(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
plaintext = plaintext[blockSize:]
ciphertext = ciphertext[blockSize:]
mul2(&c.tweak, c.isGB)
}
if remain := len(ciphertext); remain >= blockSize {
var x [blockSize]byte
if remain > blockSize {
var tt [blockSize]byte
copy(tt[:], c.tweak[:])
mul2(&tt, c.isGB)
subtle.XORBytes(x[:], ciphertext, tt[:])
c.b.Decrypt(x[:], x[:])
subtle.XORBytes(plaintext, x[:], tt[:])
//Retrieve the length of the final block
remain -= blockSize
//Copy the final ciphertext bytes
copy(x[:], ciphertext[blockSize:])
//Steal ciphertext to complete the block
copy(x[remain:], plaintext[remain:blockSize])
//Copy the final plaintext bytes
copy(plaintext[blockSize:], plaintext)
subtle.XORBytes(x[:], x[:], c.tweak[:])
c.b.Decrypt(x[:], x[:])
subtle.XORBytes(plaintext, x[:], c.tweak[:])
} else {
//The last block contains exactly 128 bits
subtle.XORBytes(plaintext, ciphertext, c.tweak[:])
c.b.Decrypt(plaintext, plaintext)
subtle.XORBytes(plaintext, plaintext, c.tweak[:])
// Maybe there are still ciphertext
mul2(&c.tweak, c.isGB)
}
}
}
// mul2Generic multiplies tweak by 2 in GF(2¹²⁸) with an irreducible polynomial of
// x¹²⁸ + x⁷ + x² + x + 1.
func mul2Generic(tweak *[blockSize]byte, isGB bool) {
var carryIn byte
if !isGB {
// the coefficient of x⁰ can be obtained by tweak[0] & 1
// the coefficient of x⁷ can be obtained by tweak[0] >> 7
// the coefficient of x¹²⁰ can be obtained by tweak[15] & 1
// the coefficient of x¹²⁷ can be obtained by tweak[15] >> 7
for j := range tweak {
carryOut := tweak[j] >> 7
tweak[j] = (tweak[j] << 1) + carryIn
carryIn = carryOut
}
if carryIn != 0 {
// If we have a carry bit then we need to subtract a multiple
// of the irreducible polynomial (x¹²⁸ + x⁷ + x² + x + 1).
// By dropping the carry bit, we're subtracting the x^128 term
// so all that remains is to subtract x⁷ + x² + x + 1.
// Subtraction (and addition) in this representation is just
// XOR.
tweak[0] ^= GF128_FDBK // 1<<7 | 1<<2 | 1<<1 | 1
}
} else {
// GB/T 17964-2021,
// the coefficient of x⁰ can be obtained by tweak[0] >> 7
// the coefficient of x⁷ can be obtained by tweak[0] & 1
// the coefficient of x¹²⁰ can be obtained by tweak[15] >> 7
// the coefficient of x¹²⁷ can be obtained by tweak[15] & 1
for j := range tweak {
carryOut := (tweak[j] << 7) & 0x80
tweak[j] = (tweak[j] >> 1) + carryIn
carryIn = carryOut
}
if carryIn != 0 {
tweak[0] ^= 0xE1 // 1<<7 | 1<<6 | 1<<5 | 1
}
}
}

View File

@ -1,6 +1,6 @@
//go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego //go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego
package cipher package xts
//go:noescape //go:noescape
func mul2(tweak *[blockSize]byte, isGB bool) func mul2(tweak *[blockSize]byte, isGB bool)

View File

@ -1,6 +1,6 @@
//go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego //go:build (amd64 || arm64 || s390x || ppc64 || ppc64le) && !purego
package cipher package xts
import ( import (
"bytes" "bytes"

View File

@ -1,6 +1,6 @@
//go:build purego || !(amd64 || arm64 || s390x || ppc64 || ppc64le) //go:build purego || !(amd64 || arm64 || s390x || ppc64 || ppc64le)
package cipher package xts
func mul2(tweak *[blockSize]byte, isGB bool) { func mul2(tweak *[blockSize]byte, isGB bool) {
mul2Generic(tweak, isGB) mul2Generic(tweak, isGB)
@ -8,7 +8,7 @@ func mul2(tweak *[blockSize]byte, isGB bool) {
func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool) { func doubleTweaks(tweak *[blockSize]byte, tweaks []byte, isGB bool) {
count := len(tweaks) >> 4 count := len(tweaks) >> 4
for i := 0; i < count; i++ { for i := range count {
copy(tweaks[blockSize*i:], tweak[:]) copy(tweaks[blockSize*i:], tweak[:])
mul2(tweak, isGB) mul2(tweak, isGB)
} }

View File

@ -1,4 +1,4 @@
package cipher package xts
import ( import (
"crypto/aes" "crypto/aes"

View File

@ -1,6 +1,6 @@
package cpuid package cpuid
import "golang.org/x/sys/cpu" import "github.com/emmansun/gmsm/internal/deps/cpu"
var ( var (
HasAES = cpu.X86.HasAES HasAES = cpu.X86.HasAES

View File

@ -2,7 +2,7 @@
package cpuid package cpuid
import "golang.org/x/sys/cpu" import "github.com/emmansun/gmsm/internal/deps/cpu"
var ( var (
HasAES = cpu.ARM64.HasAES HasAES = cpu.ARM64.HasAES

View File

@ -7,11 +7,10 @@ package cryptotest
import ( import (
"bytes" "bytes"
"crypto/cipher" "crypto/cipher"
"crypto/subtle"
"fmt" "fmt"
"strings" "strings"
"testing" "testing"
"github.com/emmansun/gmsm/internal/subtle"
) )
// Each test is executed with each of the buffer lengths in bufLens. // Each test is executed with each of the buffer lengths in bufLens.

View File

@ -0,0 +1,17 @@
// Copyright 2018 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 gc
#include "textflag.h"
//
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go
//
TEXT ·syscall6(SB),NOSPLIT,$0-88
JMP syscall·syscall6(SB)
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88
JMP syscall·rawSyscall6(SB)

View File

@ -0,0 +1,17 @@
// Copyright 2024 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 darwin && amd64 && gc
#include "textflag.h"
TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_sysctl(SB)
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
TEXT libc_sysctlbyname_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_sysctlbyname(SB)
GLOBL ·libc_sysctlbyname_trampoline_addr(SB), RODATA, $8
DATA ·libc_sysctlbyname_trampoline_addr(SB)/8, $libc_sysctlbyname_trampoline<>(SB)

View File

@ -0,0 +1,66 @@
// Copyright 2019 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.
package cpu
import (
"runtime"
)
// byteOrder is a subset of encoding/binary.ByteOrder.
type byteOrder interface {
Uint32([]byte) uint32
Uint64([]byte) uint64
}
type littleEndian struct{}
type bigEndian struct{}
func (littleEndian) Uint32(b []byte) uint32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
}
func (littleEndian) Uint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
}
func (bigEndian) Uint32(b []byte) uint32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
}
func (bigEndian) Uint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}
// hostByteOrder returns littleEndian on little-endian machines and
// bigEndian on big-endian machines.
func hostByteOrder() byteOrder {
switch runtime.GOARCH {
case "386", "amd64", "amd64p32",
"alpha",
"arm", "arm64",
"loong64",
"mipsle", "mips64le", "mips64p32le",
"nios2",
"ppc64le",
"riscv", "riscv64",
"sh":
return littleEndian{}
case "armbe", "arm64be",
"m68k",
"mips", "mips64", "mips64p32",
"ppc", "ppc64",
"s390", "s390x",
"shbe",
"sparc", "sparc64":
return bigEndian{}
}
panic("unknown architecture")
}

315
internal/deps/cpu/cpu.go Normal file
View File

@ -0,0 +1,315 @@
// Copyright 2018 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.
// Package cpu implements processor feature detection for
// various CPU architectures.
package cpu
import (
"os"
"strings"
)
// Initialized reports whether the CPU features were initialized.
//
// For some GOOS/GOARCH combinations initialization of the CPU features depends
// on reading an operating specific file, e.g. /proc/self/auxv on linux/arm
// Initialized will report false if reading the file fails.
var Initialized bool
// CacheLinePad is used to pad structs to avoid false sharing.
type CacheLinePad struct{ _ [cacheLineSize]byte }
// X86 contains the supported CPU features of the
// current X86/AMD64 platform. If the current platform
// is not X86/AMD64 then all feature flags are false.
//
// X86 is padded to avoid false sharing. Further the HasAVX
// and HasAVX2 are only set if the OS supports XMM and YMM
// registers in addition to the CPUID feature bit being set.
var X86 struct {
_ CacheLinePad
HasAES bool // AES hardware implementation (AES NI)
HasADX bool // Multi-precision add-carry instruction extensions
HasAVX bool // Advanced vector extension
HasAVX2 bool // Advanced vector extension 2
HasAVX512 bool // Advanced vector extension 512
HasAVX512F bool // Advanced vector extension 512 Foundation Instructions
HasAVX512CD bool // Advanced vector extension 512 Conflict Detection Instructions
HasAVX512ER bool // Advanced vector extension 512 Exponential and Reciprocal Instructions
HasAVX512PF bool // Advanced vector extension 512 Prefetch Instructions
HasAVX512VL bool // Advanced vector extension 512 Vector Length Extensions
HasAVX512BW bool // Advanced vector extension 512 Byte and Word Instructions
HasAVX512DQ bool // Advanced vector extension 512 Doubleword and Quadword Instructions
HasAVX512IFMA bool // Advanced vector extension 512 Integer Fused Multiply Add
HasAVX512VBMI bool // Advanced vector extension 512 Vector Byte Manipulation Instructions
HasAVX5124VNNIW bool // Advanced vector extension 512 Vector Neural Network Instructions Word variable precision
HasAVX5124FMAPS bool // Advanced vector extension 512 Fused Multiply Accumulation Packed Single precision
HasAVX512VPOPCNTDQ bool // Advanced vector extension 512 Double and quad word population count instructions
HasAVX512VPCLMULQDQ bool // Advanced vector extension 512 Vector carry-less multiply operations
HasAVX512VNNI bool // Advanced vector extension 512 Vector Neural Network Instructions
HasAVX512GFNI bool // Advanced vector extension 512 Galois field New Instructions
HasAVX512VAES bool // Advanced vector extension 512 Vector AES instructions
HasAVX512VBMI2 bool // Advanced vector extension 512 Vector Byte Manipulation Instructions 2
HasAVX512BITALG bool // Advanced vector extension 512 Bit Algorithms
HasAVX512BF16 bool // Advanced vector extension 512 BFloat16 Instructions
HasAMXTile bool // Advanced Matrix Extension Tile instructions
HasAMXInt8 bool // Advanced Matrix Extension Int8 instructions
HasAMXBF16 bool // Advanced Matrix Extension BFloat16 instructions
HasBMI1 bool // Bit manipulation instruction set 1
HasBMI2 bool // Bit manipulation instruction set 2
HasCX16 bool // Compare and exchange 16 Bytes
HasERMS bool // Enhanced REP for MOVSB and STOSB
HasFMA bool // Fused-multiply-add instructions
HasOSXSAVE bool // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
HasPCLMULQDQ bool // PCLMULQDQ instruction - most often used for AES-GCM
HasPOPCNT bool // Hamming weight instruction POPCNT.
HasRDRAND bool // RDRAND instruction (on-chip random number generator)
HasRDSEED bool // RDSEED instruction (on-chip random number generator)
HasSSE2 bool // Streaming SIMD extension 2 (always available on amd64)
HasSSE3 bool // Streaming SIMD extension 3
HasSSSE3 bool // Supplemental streaming SIMD extension 3
HasSSE41 bool // Streaming SIMD extension 4 and 4.1
HasSSE42 bool // Streaming SIMD extension 4 and 4.2
HasAVXIFMA bool // Advanced vector extension Integer Fused Multiply Add
HasAVXVNNI bool // Advanced vector extension Vector Neural Network Instructions
HasAVXVNNIInt8 bool // Advanced vector extension Vector Neural Network Int8 instructions
_ CacheLinePad
}
// ARM64 contains the supported CPU features of the
// current ARMv8(aarch64) platform. If the current platform
// is not arm64 then all feature flags are false.
var ARM64 struct {
_ CacheLinePad
HasFP bool // Floating-point instruction set (always available)
HasASIMD bool // Advanced SIMD (always available)
HasEVTSTRM bool // Event stream support
HasAES bool // AES hardware implementation
HasPMULL bool // Polynomial multiplication instruction set
HasSHA1 bool // SHA1 hardware implementation
HasSHA2 bool // SHA2 hardware implementation
HasCRC32 bool // CRC32 hardware implementation
HasATOMICS bool // Atomic memory operation instruction set
HasFPHP bool // Half precision floating-point instruction set
HasASIMDHP bool // Advanced SIMD half precision instruction set
HasCPUID bool // CPUID identification scheme registers
HasASIMDRDM bool // Rounding double multiply add/subtract instruction set
HasJSCVT bool // Javascript conversion from floating-point to integer
HasFCMA bool // Floating-point multiplication and addition of complex numbers
HasLRCPC bool // Release Consistent processor consistent support
HasDCPOP bool // Persistent memory support
HasSHA3 bool // SHA3 hardware implementation
HasSM3 bool // SM3 hardware implementation
HasSM4 bool // SM4 hardware implementation
HasASIMDDP bool // Advanced SIMD double precision instruction set
HasSHA512 bool // SHA512 hardware implementation
HasSVE bool // Scalable Vector Extensions
HasSVE2 bool // Scalable Vector Extensions 2
HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32
HasDIT bool // Data Independent Timing support
HasI8MM bool // Advanced SIMD Int8 matrix multiplication instructions
_ CacheLinePad
}
// ARM contains the supported CPU features of the current ARM (32-bit) platform.
// All feature flags are false if:
// 1. the current platform is not arm, or
// 2. the current operating system is not Linux.
var ARM struct {
_ CacheLinePad
HasSWP bool // SWP instruction support
HasHALF bool // Half-word load and store support
HasTHUMB bool // ARM Thumb instruction set
Has26BIT bool // Address space limited to 26-bits
HasFASTMUL bool // 32-bit operand, 64-bit result multiplication support
HasFPA bool // Floating point arithmetic support
HasVFP bool // Vector floating point support
HasEDSP bool // DSP Extensions support
HasJAVA bool // Java instruction set
HasIWMMXT bool // Intel Wireless MMX technology support
HasCRUNCH bool // MaverickCrunch context switching and handling
HasTHUMBEE bool // Thumb EE instruction set
HasNEON bool // NEON instruction set
HasVFPv3 bool // Vector floating point version 3 support
HasVFPv3D16 bool // Vector floating point version 3 D8-D15
HasTLS bool // Thread local storage support
HasVFPv4 bool // Vector floating point version 4 support
HasIDIVA bool // Integer divide instruction support in ARM mode
HasIDIVT bool // Integer divide instruction support in Thumb mode
HasVFPD32 bool // Vector floating point version 3 D15-D31
HasLPAE bool // Large Physical Address Extensions
HasEVTSTRM bool // Event stream support
HasAES bool // AES hardware implementation
HasPMULL bool // Polynomial multiplication instruction set
HasSHA1 bool // SHA1 hardware implementation
HasSHA2 bool // SHA2 hardware implementation
HasCRC32 bool // CRC32 hardware implementation
_ CacheLinePad
}
// MIPS64X contains the supported CPU features of the current mips64/mips64le
// platforms. If the current platform is not mips64/mips64le or the current
// operating system is not Linux then all feature flags are false.
var MIPS64X struct {
_ CacheLinePad
HasMSA bool // MIPS SIMD architecture
_ CacheLinePad
}
// PPC64 contains the supported CPU features of the current ppc64/ppc64le platforms.
// If the current platform is not ppc64/ppc64le then all feature flags are false.
//
// For ppc64/ppc64le, it is safe to check only for ISA level starting on ISA v3.00,
// since there are no optional categories. There are some exceptions that also
// require kernel support to work (DARN, SCV), so there are feature bits for
// those as well. The struct is padded to avoid false sharing.
var PPC64 struct {
_ CacheLinePad
HasDARN bool // Hardware random number generator (requires kernel enablement)
HasSCV bool // Syscall vectored (requires kernel enablement)
IsPOWER8 bool // ISA v2.07 (POWER8)
IsPOWER9 bool // ISA v3.00 (POWER9), implies IsPOWER8
_ CacheLinePad
}
// S390X contains the supported CPU features of the current IBM Z
// (s390x) platform. If the current platform is not IBM Z then all
// feature flags are false.
//
// S390X is padded to avoid false sharing. Further HasVX is only set
// if the OS supports vector registers in addition to the STFLE
// feature bit being set.
var S390X struct {
_ CacheLinePad
HasZARCH bool // z/Architecture mode is active [mandatory]
HasSTFLE bool // store facility list extended
HasLDISP bool // long (20-bit) displacements
HasEIMM bool // 32-bit immediates
HasDFP bool // decimal floating point
HasETF3EH bool // ETF-3 enhanced
HasMSA bool // message security assist (CPACF)
HasAES bool // KM-AES{128,192,256} functions
HasAESCBC bool // KMC-AES{128,192,256} functions
HasAESCTR bool // KMCTR-AES{128,192,256} functions
HasAESGCM bool // KMA-GCM-AES{128,192,256} functions
HasGHASH bool // KIMD-GHASH function
HasSHA1 bool // K{I,L}MD-SHA-1 functions
HasSHA256 bool // K{I,L}MD-SHA-256 functions
HasSHA512 bool // K{I,L}MD-SHA-512 functions
HasSHA3 bool // K{I,L}MD-SHA3-{224,256,384,512} and K{I,L}MD-SHAKE-{128,256} functions
HasVX bool // vector facility
HasVXE bool // vector-enhancements facility 1
_ CacheLinePad
}
// RISCV64 contains the supported CPU features and performance characteristics for riscv64
// platforms. The booleans in RISCV64, with the exception of HasFastMisaligned, indicate
// the presence of RISC-V extensions.
//
// It is safe to assume that all the RV64G extensions are supported and so they are omitted from
// this structure. As riscv64 Go programs require at least RV64G, the code that populates
// this structure cannot run successfully if some of the RV64G extensions are missing.
// The struct is padded to avoid false sharing.
var RISCV64 struct {
_ CacheLinePad
HasFastMisaligned bool // Fast misaligned accesses
HasC bool // Compressed instruction-set extension
HasV bool // Vector extension compatible with RVV 1.0
HasZba bool // Address generation instructions extension
HasZbb bool // Basic bit-manipulation extension
HasZbs bool // Single-bit instructions extension
_ CacheLinePad
}
func init() {
archInit()
initOptions()
processOptions()
}
// options contains the cpu debug options that can be used in GODEBUG.
// Options are arch dependent and are added by the arch specific initOptions functions.
// Features that are mandatory for the specific GOARCH should have the Required field set
// (e.g. SSE2 on amd64).
var options []option
// Option names should be lower case. e.g. avx instead of AVX.
type option struct {
Name string
Feature *bool
Specified bool // whether feature value was specified in GODEBUG
Enable bool // whether feature should be enabled
Required bool // whether feature is mandatory and can not be disabled
}
func processOptions() {
env := os.Getenv("GODEBUG")
field:
for env != "" {
field := ""
i := strings.IndexByte(env, ',')
if i < 0 {
field, env = env, ""
} else {
field, env = env[:i], env[i+1:]
}
if len(field) < 4 || field[:4] != "cpu." {
continue
}
i = strings.IndexByte(field, '=')
if i < 0 {
print("GODEBUG sys/cpu: no value specified for \"", field, "\"\n")
continue
}
key, value := field[4:i], field[i+1:] // e.g. "SSE2", "on"
var enable bool
switch value {
case "on":
enable = true
case "off":
enable = false
default:
print("GODEBUG sys/cpu: value \"", value, "\" not supported for cpu option \"", key, "\"\n")
continue field
}
if key == "all" {
for i := range options {
options[i].Specified = true
options[i].Enable = enable || options[i].Required
}
continue field
}
for i := range options {
if options[i].Name == key {
options[i].Specified = true
options[i].Enable = enable
continue field
}
}
print("GODEBUG sys/cpu: unknown cpu feature \"", key, "\"\n")
}
for _, o := range options {
if !o.Specified {
continue
}
if o.Enable && !*o.Feature {
print("GODEBUG sys/cpu: can not enable \"", o.Name, "\", missing CPU support\n")
continue
}
if !o.Enable && o.Required {
print("GODEBUG sys/cpu: can not disable \"", o.Name, "\", required CPU feature\n")
continue
}
*o.Feature = o.Enable
}
}

View File

@ -0,0 +1,33 @@
// Copyright 2019 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 aix
package cpu
const (
// getsystemcfg constants
_SC_IMPL = 2
_IMPL_POWER8 = 0x10000
_IMPL_POWER9 = 0x20000
)
func archInit() {
impl := getsystemcfg(_SC_IMPL)
if impl&_IMPL_POWER8 != 0 {
PPC64.IsPOWER8 = true
}
if impl&_IMPL_POWER9 != 0 {
PPC64.IsPOWER8 = true
PPC64.IsPOWER9 = true
}
Initialized = true
}
func getsystemcfg(label int) (n uint64) {
r0, _ := callgetsystemcfg(label)
n = uint64(r0)
return
}

View File

@ -0,0 +1,72 @@
// Copyright 2018 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.
package cpu
const cacheLineSize = 32
// HWCAP/HWCAP2 bits.
// These are specific to Linux.
const (
hwcap_SWP = 1 << 0
hwcap_HALF = 1 << 1
hwcap_THUMB = 1 << 2
hwcap_26BIT = 1 << 3
hwcap_FAST_MULT = 1 << 4
hwcap_FPA = 1 << 5
hwcap_VFP = 1 << 6
hwcap_EDSP = 1 << 7
hwcap_JAVA = 1 << 8
hwcap_IWMMXT = 1 << 9
hwcap_CRUNCH = 1 << 10
hwcap_THUMBEE = 1 << 11
hwcap_NEON = 1 << 12
hwcap_VFPv3 = 1 << 13
hwcap_VFPv3D16 = 1 << 14
hwcap_TLS = 1 << 15
hwcap_VFPv4 = 1 << 16
hwcap_IDIVA = 1 << 17
hwcap_IDIVT = 1 << 18
hwcap_VFPD32 = 1 << 19
hwcap_LPAE = 1 << 20
hwcap_EVTSTRM = 1 << 21
hwcap2_AES = 1 << 0
hwcap2_PMULL = 1 << 1
hwcap2_SHA1 = 1 << 2
hwcap2_SHA2 = 1 << 3
hwcap2_CRC32 = 1 << 4
)
func initOptions() {
options = []option{
{Name: "pmull", Feature: &ARM.HasPMULL},
{Name: "sha1", Feature: &ARM.HasSHA1},
{Name: "sha2", Feature: &ARM.HasSHA2},
{Name: "swp", Feature: &ARM.HasSWP},
{Name: "thumb", Feature: &ARM.HasTHUMB},
{Name: "thumbee", Feature: &ARM.HasTHUMBEE},
{Name: "tls", Feature: &ARM.HasTLS},
{Name: "vfp", Feature: &ARM.HasVFP},
{Name: "vfpd32", Feature: &ARM.HasVFPD32},
{Name: "vfpv3", Feature: &ARM.HasVFPv3},
{Name: "vfpv3d16", Feature: &ARM.HasVFPv3D16},
{Name: "vfpv4", Feature: &ARM.HasVFPv4},
{Name: "half", Feature: &ARM.HasHALF},
{Name: "26bit", Feature: &ARM.Has26BIT},
{Name: "fastmul", Feature: &ARM.HasFASTMUL},
{Name: "fpa", Feature: &ARM.HasFPA},
{Name: "edsp", Feature: &ARM.HasEDSP},
{Name: "java", Feature: &ARM.HasJAVA},
{Name: "iwmmxt", Feature: &ARM.HasIWMMXT},
{Name: "crunch", Feature: &ARM.HasCRUNCH},
{Name: "neon", Feature: &ARM.HasNEON},
{Name: "idivt", Feature: &ARM.HasIDIVT},
{Name: "idiva", Feature: &ARM.HasIDIVA},
{Name: "lpae", Feature: &ARM.HasLPAE},
{Name: "evtstrm", Feature: &ARM.HasEVTSTRM},
{Name: "aes", Feature: &ARM.HasAES},
{Name: "crc32", Feature: &ARM.HasCRC32},
}
}

View File

@ -0,0 +1,194 @@
// Copyright 2019 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.
package cpu
import "runtime"
// cacheLineSize is used to prevent false sharing of cache lines.
// We choose 128 because Apple Silicon, a.k.a. M1, has 128-byte cache line size.
// It doesn't cost much and is much more future-proof.
const cacheLineSize = 128
func initOptions() {
options = []option{
{Name: "fp", Feature: &ARM64.HasFP},
{Name: "asimd", Feature: &ARM64.HasASIMD},
{Name: "evstrm", Feature: &ARM64.HasEVTSTRM},
{Name: "aes", Feature: &ARM64.HasAES},
{Name: "fphp", Feature: &ARM64.HasFPHP},
{Name: "jscvt", Feature: &ARM64.HasJSCVT},
{Name: "lrcpc", Feature: &ARM64.HasLRCPC},
{Name: "pmull", Feature: &ARM64.HasPMULL},
{Name: "sha1", Feature: &ARM64.HasSHA1},
{Name: "sha2", Feature: &ARM64.HasSHA2},
{Name: "sha3", Feature: &ARM64.HasSHA3},
{Name: "sha512", Feature: &ARM64.HasSHA512},
{Name: "sm3", Feature: &ARM64.HasSM3},
{Name: "sm4", Feature: &ARM64.HasSM4},
{Name: "sve", Feature: &ARM64.HasSVE},
{Name: "sve2", Feature: &ARM64.HasSVE2},
{Name: "crc32", Feature: &ARM64.HasCRC32},
{Name: "atomics", Feature: &ARM64.HasATOMICS},
{Name: "asimdhp", Feature: &ARM64.HasASIMDHP},
{Name: "cpuid", Feature: &ARM64.HasCPUID},
{Name: "asimrdm", Feature: &ARM64.HasASIMDRDM},
{Name: "fcma", Feature: &ARM64.HasFCMA},
{Name: "dcpop", Feature: &ARM64.HasDCPOP},
{Name: "asimddp", Feature: &ARM64.HasASIMDDP},
{Name: "asimdfhm", Feature: &ARM64.HasASIMDFHM},
{Name: "dit", Feature: &ARM64.HasDIT},
{Name: "i8mm", Feature: &ARM64.HasI8MM},
}
}
func archInit() {
switch runtime.GOOS {
case "freebsd":
readARM64Registers()
case "linux", "netbsd", "openbsd":
doinit()
default:
// Many platforms don't seem to allow reading these registers.
setMinimalFeatures()
}
}
// setMinimalFeatures fakes the minimal ARM64 features expected by
// TestARM64minimalFeatures.
func setMinimalFeatures() {
ARM64.HasASIMD = true
ARM64.HasFP = true
}
func readARM64Registers() {
Initialized = true
parseARM64SystemRegisters(getisar0(), getisar1(), getpfr0())
}
func parseARM64SystemRegisters(isar0, isar1, pfr0 uint64) {
// ID_AA64ISAR0_EL1
switch extractBits(isar0, 4, 7) {
case 1:
ARM64.HasAES = true
case 2:
ARM64.HasAES = true
ARM64.HasPMULL = true
}
switch extractBits(isar0, 8, 11) {
case 1:
ARM64.HasSHA1 = true
}
switch extractBits(isar0, 12, 15) {
case 1:
ARM64.HasSHA2 = true
case 2:
ARM64.HasSHA2 = true
ARM64.HasSHA512 = true
}
switch extractBits(isar0, 16, 19) {
case 1:
ARM64.HasCRC32 = true
}
switch extractBits(isar0, 20, 23) {
case 2:
ARM64.HasATOMICS = true
}
switch extractBits(isar0, 28, 31) {
case 1:
ARM64.HasASIMDRDM = true
}
switch extractBits(isar0, 32, 35) {
case 1:
ARM64.HasSHA3 = true
}
switch extractBits(isar0, 36, 39) {
case 1:
ARM64.HasSM3 = true
}
switch extractBits(isar0, 40, 43) {
case 1:
ARM64.HasSM4 = true
}
switch extractBits(isar0, 44, 47) {
case 1:
ARM64.HasASIMDDP = true
}
// ID_AA64ISAR1_EL1
switch extractBits(isar1, 0, 3) {
case 1:
ARM64.HasDCPOP = true
}
switch extractBits(isar1, 12, 15) {
case 1:
ARM64.HasJSCVT = true
}
switch extractBits(isar1, 16, 19) {
case 1:
ARM64.HasFCMA = true
}
switch extractBits(isar1, 20, 23) {
case 1:
ARM64.HasLRCPC = true
}
switch extractBits(isar1, 52, 55) {
case 1:
ARM64.HasI8MM = true
}
// ID_AA64PFR0_EL1
switch extractBits(pfr0, 16, 19) {
case 0:
ARM64.HasFP = true
case 1:
ARM64.HasFP = true
ARM64.HasFPHP = true
}
switch extractBits(pfr0, 20, 23) {
case 0:
ARM64.HasASIMD = true
case 1:
ARM64.HasASIMD = true
ARM64.HasASIMDHP = true
}
switch extractBits(pfr0, 32, 35) {
case 1:
ARM64.HasSVE = true
parseARM64SVERegister(getzfr0())
}
switch extractBits(pfr0, 48, 51) {
case 1:
ARM64.HasDIT = true
}
}
func parseARM64SVERegister(zfr0 uint64) {
switch extractBits(zfr0, 0, 3) {
case 1:
ARM64.HasSVE2 = true
}
}
func extractBits(data uint64, start, end uint) uint {
return (uint)(data>>start) & ((1 << (end - start + 1)) - 1)
}

View File

@ -0,0 +1,39 @@
// Copyright 2019 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 gc
#include "textflag.h"
// func getisar0() uint64
TEXT ·getisar0(SB),NOSPLIT,$0-8
// get Instruction Set Attributes 0 into x0
// mrs x0, ID_AA64ISAR0_EL1 = d5380600
WORD $0xd5380600
MOVD R0, ret+0(FP)
RET
// func getisar1() uint64
TEXT ·getisar1(SB),NOSPLIT,$0-8
// get Instruction Set Attributes 1 into x0
// mrs x0, ID_AA64ISAR1_EL1 = d5380620
WORD $0xd5380620
MOVD R0, ret+0(FP)
RET
// func getpfr0() uint64
TEXT ·getpfr0(SB),NOSPLIT,$0-8
// get Processor Feature Register 0 into x0
// mrs x0, ID_AA64PFR0_EL1 = d5380400
WORD $0xd5380400
MOVD R0, ret+0(FP)
RET
// func getzfr0() uint64
TEXT ·getzfr0(SB),NOSPLIT,$0-8
// get SVE Feature Register 0 into x0
// mrs x0, ID_AA64ZFR0_EL1 = d5380480
WORD $0xd5380480
MOVD R0, ret+0(FP)
RET

View File

@ -0,0 +1,61 @@
// Copyright 2024 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 darwin && amd64 && gc
package cpu
// darwinSupportsAVX512 checks Darwin kernel for AVX512 support via sysctl
// call (see issue 43089). It also restricts AVX512 support for Darwin to
// kernel version 21.3.0 (MacOS 12.2.0) or later (see issue 49233).
//
// Background:
// Darwin implements a special mechanism to economize on thread state when
// AVX512 specific registers are not in use. This scheme minimizes state when
// preempting threads that haven't yet used any AVX512 instructions, but adds
// special requirements to check for AVX512 hardware support at runtime (e.g.
// via sysctl call or commpage inspection). See issue 43089 and link below for
// full background:
// https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.1.10/osfmk/i386/fpu.c#L214-L240
//
// Additionally, all versions of the Darwin kernel from 19.6.0 through 21.2.0
// (corresponding to MacOS 10.15.6 - 12.1) have a bug that can cause corruption
// of the AVX512 mask registers (K0-K7) upon signal return. For this reason
// AVX512 is considered unsafe to use on Darwin for kernel versions prior to
// 21.3.0, where a fix has been confirmed. See issue 49233 for full background.
func darwinSupportsAVX512() bool {
return darwinSysctlEnabled([]byte("hw.optional.avx512f\x00")) && darwinKernelVersionCheck(21, 3, 0)
}
// Ensure Darwin kernel version is at least major.minor.patch, avoiding dependencies
func darwinKernelVersionCheck(major, minor, patch int) bool {
var release [256]byte
err := darwinOSRelease(&release)
if err != nil {
return false
}
var mmp [3]int
c := 0
Loop:
for _, b := range release[:] {
switch {
case b >= '0' && b <= '9':
mmp[c] = 10*mmp[c] + int(b-'0')
case b == '.':
c++
if c > 2 {
return false
}
case b == 0:
break Loop
default:
return false
}
}
if c != 2 {
return false
}
return mmp[0] > major || mmp[0] == major && (mmp[1] > minor || mmp[1] == minor && mmp[2] >= patch)
}

View File

@ -0,0 +1,12 @@
// Copyright 2019 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 gc
package cpu
func getisar0() uint64
func getisar1() uint64
func getpfr0() uint64
func getzfr0() uint64

View File

@ -0,0 +1,21 @@
// Copyright 2019 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 gc
package cpu
// haveAsmFunctions reports whether the other functions in this file can
// be safely called.
func haveAsmFunctions() bool { return true }
// The following feature detection functions are defined in cpu_s390x.s.
// They are likely to be expensive to call so the results should be cached.
func stfle() facilityList
func kmQuery() queryResult
func kmcQuery() queryResult
func kmctrQuery() queryResult
func kmaQuery() queryResult
func kimdQuery() queryResult
func klmdQuery() queryResult

View File

@ -0,0 +1,15 @@
// Copyright 2018 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 (386 || amd64 || amd64p32) && gc
package cpu
// cpuid is implemented in cpu_gc_x86.s for gc compiler
// and in cpu_gccgo.c for gccgo.
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
// xgetbv with ecx = 0 is implemented in cpu_gc_x86.s for gc compiler
// and in cpu_gccgo.c for gccgo.
func xgetbv() (eax, edx uint32)

View File

@ -0,0 +1,26 @@
// Copyright 2018 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 (386 || amd64 || amd64p32) && gc
#include "textflag.h"
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
TEXT ·cpuid(SB), NOSPLIT, $0-24
MOVL eaxArg+0(FP), AX
MOVL ecxArg+4(FP), CX
CPUID
MOVL AX, eax+8(FP)
MOVL BX, ebx+12(FP)
MOVL CX, ecx+16(FP)
MOVL DX, edx+20(FP)
RET
// func xgetbv() (eax, edx uint32)
TEXT ·xgetbv(SB), NOSPLIT, $0-8
MOVL $0, CX
XGETBV
MOVL AX, eax+0(FP)
MOVL DX, edx+4(FP)
RET

View File

@ -0,0 +1,11 @@
// Copyright 2019 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 gccgo
package cpu
func getisar0() uint64 { return 0 }
func getisar1() uint64 { return 0 }
func getpfr0() uint64 { return 0 }

View File

@ -0,0 +1,22 @@
// Copyright 2019 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 gccgo
package cpu
// haveAsmFunctions reports whether the other functions in this file can
// be safely called.
func haveAsmFunctions() bool { return false }
// TODO(mundaym): the following feature detection functions are currently
// stubs. See https://golang.org/cl/162887 for how to fix this.
// They are likely to be expensive to call so the results should be cached.
func stfle() facilityList { panic("not implemented for gccgo") }
func kmQuery() queryResult { panic("not implemented for gccgo") }
func kmcQuery() queryResult { panic("not implemented for gccgo") }
func kmctrQuery() queryResult { panic("not implemented for gccgo") }
func kmaQuery() queryResult { panic("not implemented for gccgo") }
func kimdQuery() queryResult { panic("not implemented for gccgo") }
func klmdQuery() queryResult { panic("not implemented for gccgo") }

View File

@ -0,0 +1,37 @@
// Copyright 2018 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 (386 || amd64 || amd64p32) && gccgo
#include <cpuid.h>
#include <stdint.h>
#include <x86intrin.h>
// Need to wrap __get_cpuid_count because it's declared as static.
int
gccgoGetCpuidCount(uint32_t leaf, uint32_t subleaf,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx)
{
return __get_cpuid_count(leaf, subleaf, eax, ebx, ecx, edx);
}
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC push_options
#pragma GCC target("xsave")
#pragma clang attribute push (__attribute__((target("xsave"))), apply_to=function)
// xgetbv reads the contents of an XCR (Extended Control Register)
// specified in the ECX register into registers EDX:EAX.
// Currently, the only supported value for XCR is 0.
void
gccgoXgetbv(uint32_t *eax, uint32_t *edx)
{
uint64_t v = _xgetbv(0);
*eax = v & 0xffffffff;
*edx = v >> 32;
}
#pragma clang attribute pop
#pragma GCC pop_options

View File

@ -0,0 +1,25 @@
// Copyright 2018 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 (386 || amd64 || amd64p32) && gccgo
package cpu
//extern gccgoGetCpuidCount
func gccgoGetCpuidCount(eaxArg, ecxArg uint32, eax, ebx, ecx, edx *uint32)
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) {
var a, b, c, d uint32
gccgoGetCpuidCount(eaxArg, ecxArg, &a, &b, &c, &d)
return a, b, c, d
}
//extern gccgoXgetbv
func gccgoXgetbv(eax, edx *uint32)
func xgetbv() (eax, edx uint32) {
var a, d uint32
gccgoXgetbv(&a, &d)
return a, d
}

View File

@ -0,0 +1,15 @@
// Copyright 2018 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 !386 && !amd64 && !amd64p32 && !arm64
package cpu
func archInit() {
if err := readHWCAP(); err != nil {
return
}
doinit()
Initialized = true
}

View File

@ -0,0 +1,39 @@
// Copyright 2019 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.
package cpu
func doinit() {
ARM.HasSWP = isSet(hwCap, hwcap_SWP)
ARM.HasHALF = isSet(hwCap, hwcap_HALF)
ARM.HasTHUMB = isSet(hwCap, hwcap_THUMB)
ARM.Has26BIT = isSet(hwCap, hwcap_26BIT)
ARM.HasFASTMUL = isSet(hwCap, hwcap_FAST_MULT)
ARM.HasFPA = isSet(hwCap, hwcap_FPA)
ARM.HasVFP = isSet(hwCap, hwcap_VFP)
ARM.HasEDSP = isSet(hwCap, hwcap_EDSP)
ARM.HasJAVA = isSet(hwCap, hwcap_JAVA)
ARM.HasIWMMXT = isSet(hwCap, hwcap_IWMMXT)
ARM.HasCRUNCH = isSet(hwCap, hwcap_CRUNCH)
ARM.HasTHUMBEE = isSet(hwCap, hwcap_THUMBEE)
ARM.HasNEON = isSet(hwCap, hwcap_NEON)
ARM.HasVFPv3 = isSet(hwCap, hwcap_VFPv3)
ARM.HasVFPv3D16 = isSet(hwCap, hwcap_VFPv3D16)
ARM.HasTLS = isSet(hwCap, hwcap_TLS)
ARM.HasVFPv4 = isSet(hwCap, hwcap_VFPv4)
ARM.HasIDIVA = isSet(hwCap, hwcap_IDIVA)
ARM.HasIDIVT = isSet(hwCap, hwcap_IDIVT)
ARM.HasVFPD32 = isSet(hwCap, hwcap_VFPD32)
ARM.HasLPAE = isSet(hwCap, hwcap_LPAE)
ARM.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM)
ARM.HasAES = isSet(hwCap2, hwcap2_AES)
ARM.HasPMULL = isSet(hwCap2, hwcap2_PMULL)
ARM.HasSHA1 = isSet(hwCap2, hwcap2_SHA1)
ARM.HasSHA2 = isSet(hwCap2, hwcap2_SHA2)
ARM.HasCRC32 = isSet(hwCap2, hwcap2_CRC32)
}
func isSet(hwc uint, value uint) bool {
return hwc&value != 0
}

View File

@ -0,0 +1,120 @@
// Copyright 2018 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.
package cpu
import (
"strings"
"syscall"
)
// HWCAP/HWCAP2 bits. These are exposed by Linux.
const (
hwcap_FP = 1 << 0
hwcap_ASIMD = 1 << 1
hwcap_EVTSTRM = 1 << 2
hwcap_AES = 1 << 3
hwcap_PMULL = 1 << 4
hwcap_SHA1 = 1 << 5
hwcap_SHA2 = 1 << 6
hwcap_CRC32 = 1 << 7
hwcap_ATOMICS = 1 << 8
hwcap_FPHP = 1 << 9
hwcap_ASIMDHP = 1 << 10
hwcap_CPUID = 1 << 11
hwcap_ASIMDRDM = 1 << 12
hwcap_JSCVT = 1 << 13
hwcap_FCMA = 1 << 14
hwcap_LRCPC = 1 << 15
hwcap_DCPOP = 1 << 16
hwcap_SHA3 = 1 << 17
hwcap_SM3 = 1 << 18
hwcap_SM4 = 1 << 19
hwcap_ASIMDDP = 1 << 20
hwcap_SHA512 = 1 << 21
hwcap_SVE = 1 << 22
hwcap_ASIMDFHM = 1 << 23
hwcap_DIT = 1 << 24
hwcap2_SVE2 = 1 << 1
hwcap2_I8MM = 1 << 13
)
// linuxKernelCanEmulateCPUID reports whether we're running
// on Linux 4.11+. Ideally we'd like to ask the question about
// whether the current kernel contains
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=77c97b4ee21290f5f083173d957843b615abbff2
// but the version number will have to do.
func linuxKernelCanEmulateCPUID() bool {
var un syscall.Utsname
syscall.Uname(&un)
var sb strings.Builder
for _, b := range un.Release[:] {
if b == 0 {
break
}
sb.WriteByte(byte(b))
}
major, minor, _, ok := parseRelease(sb.String())
return ok && (major > 4 || major == 4 && minor >= 11)
}
func doinit() {
if err := readHWCAP(); err != nil {
// We failed to read /proc/self/auxv. This can happen if the binary has
// been given extra capabilities(7) with /bin/setcap.
//
// When this happens, we have two options. If the Linux kernel is new
// enough (4.11+), we can read the arm64 registers directly which'll
// trap into the kernel and then return back to userspace.
//
// But on older kernels, such as Linux 4.4.180 as used on many Synology
// devices, calling readARM64Registers (specifically getisar0) will
// cause a SIGILL and we'll die. So for older kernels, parse /proc/cpuinfo
// instead.
//
// See golang/go#57336.
if linuxKernelCanEmulateCPUID() {
readARM64Registers()
} else {
readLinuxProcCPUInfo()
}
return
}
// HWCAP feature bits
ARM64.HasFP = isSet(hwCap, hwcap_FP)
ARM64.HasASIMD = isSet(hwCap, hwcap_ASIMD)
ARM64.HasEVTSTRM = isSet(hwCap, hwcap_EVTSTRM)
ARM64.HasAES = isSet(hwCap, hwcap_AES)
ARM64.HasPMULL = isSet(hwCap, hwcap_PMULL)
ARM64.HasSHA1 = isSet(hwCap, hwcap_SHA1)
ARM64.HasSHA2 = isSet(hwCap, hwcap_SHA2)
ARM64.HasCRC32 = isSet(hwCap, hwcap_CRC32)
ARM64.HasATOMICS = isSet(hwCap, hwcap_ATOMICS)
ARM64.HasFPHP = isSet(hwCap, hwcap_FPHP)
ARM64.HasASIMDHP = isSet(hwCap, hwcap_ASIMDHP)
ARM64.HasCPUID = isSet(hwCap, hwcap_CPUID)
ARM64.HasASIMDRDM = isSet(hwCap, hwcap_ASIMDRDM)
ARM64.HasJSCVT = isSet(hwCap, hwcap_JSCVT)
ARM64.HasFCMA = isSet(hwCap, hwcap_FCMA)
ARM64.HasLRCPC = isSet(hwCap, hwcap_LRCPC)
ARM64.HasDCPOP = isSet(hwCap, hwcap_DCPOP)
ARM64.HasSHA3 = isSet(hwCap, hwcap_SHA3)
ARM64.HasSM3 = isSet(hwCap, hwcap_SM3)
ARM64.HasSM4 = isSet(hwCap, hwcap_SM4)
ARM64.HasASIMDDP = isSet(hwCap, hwcap_ASIMDDP)
ARM64.HasSHA512 = isSet(hwCap, hwcap_SHA512)
ARM64.HasSVE = isSet(hwCap, hwcap_SVE)
ARM64.HasASIMDFHM = isSet(hwCap, hwcap_ASIMDFHM)
ARM64.HasDIT = isSet(hwCap, hwcap_DIT)
// HWCAP2 feature bits
ARM64.HasSVE2 = isSet(hwCap2, hwcap2_SVE2)
ARM64.HasI8MM = isSet(hwCap2, hwcap2_I8MM)
}
func isSet(hwc uint, value uint) bool {
return hwc&value != 0
}

View File

@ -0,0 +1,22 @@
// Copyright 2020 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 linux && (mips64 || mips64le)
package cpu
// HWCAP bits. These are exposed by the Linux kernel 5.4.
const (
// CPU features
hwcap_MIPS_MSA = 1 << 1
)
func doinit() {
// HWCAP feature bits
MIPS64X.HasMSA = isSet(hwCap, hwcap_MIPS_MSA)
}
func isSet(hwc uint, value uint) bool {
return hwc&value != 0
}

View File

@ -0,0 +1,9 @@
// Copyright 2019 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 linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64
package cpu
func doinit() {}

View File

@ -0,0 +1,30 @@
// Copyright 2018 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 linux && (ppc64 || ppc64le)
package cpu
// HWCAP/HWCAP2 bits. These are exposed by the kernel.
const (
// ISA Level
_PPC_FEATURE2_ARCH_2_07 = 0x80000000
_PPC_FEATURE2_ARCH_3_00 = 0x00800000
// CPU features
_PPC_FEATURE2_DARN = 0x00200000
_PPC_FEATURE2_SCV = 0x00100000
)
func doinit() {
// HWCAP2 feature bits
PPC64.IsPOWER8 = isSet(hwCap2, _PPC_FEATURE2_ARCH_2_07)
PPC64.IsPOWER9 = isSet(hwCap2, _PPC_FEATURE2_ARCH_3_00)
PPC64.HasDARN = isSet(hwCap2, _PPC_FEATURE2_DARN)
PPC64.HasSCV = isSet(hwCap2, _PPC_FEATURE2_SCV)
}
func isSet(hwc uint, value uint) bool {
return hwc&value != 0
}

View File

@ -0,0 +1,137 @@
// Copyright 2024 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.
package cpu
import (
"syscall"
"unsafe"
)
// RISC-V extension discovery code for Linux. The approach here is to first try the riscv_hwprobe
// syscall falling back to HWCAP to check for the C extension if riscv_hwprobe is not available.
//
// A note on detection of the Vector extension using HWCAP.
//
// Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5.
// Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe
// syscall is not available then neither is the Vector extension (which needs kernel support).
// The riscv_hwprobe syscall should then be all we need to detect the Vector extension.
// However, some RISC-V board manufacturers ship boards with an older kernel on top of which
// they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe
// patches. These kernels advertise support for the Vector extension using HWCAP. Falling
// back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not
// bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option.
//
// Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by
// RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board
// and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified
// 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use
// it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector
// extension are binary incompatible. HWCAP can then not be used in isolation to populate the
// HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0.
//
// There is a way at runtime to distinguish between versions 0.7.1 and 1.0 of the Vector
// specification by issuing a RVV 1.0 vsetvli instruction and checking the vill bit of the vtype
// register. This check would allow us to safely detect version 1.0 of the Vector extension
// with HWCAP, if riscv_hwprobe were not available. However, the check cannot
// be added until the assembler supports the Vector instructions.
//
// Note the riscv_hwprobe syscall does not suffer from these ambiguities by design as all of the
// extensions it advertises support for are explicitly versioned. It's also worth noting that
// the riscv_hwprobe syscall is the only way to detect multi-letter RISC-V extensions, e.g., Zba.
// These cannot be detected using HWCAP and so riscv_hwprobe must be used to detect the majority
// of RISC-V extensions.
//
// Please see https://docs.kernel.org/arch/riscv/hwprobe.html for more information.
// golang.org/x/sys/cpu is not allowed to depend on golang.org/x/sys/unix so we must
// reproduce the constants, types and functions needed to make the riscv_hwprobe syscall
// here.
const (
// Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go.
riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4
riscv_HWPROBE_IMA_C = 0x2
riscv_HWPROBE_IMA_V = 0x4
riscv_HWPROBE_EXT_ZBA = 0x8
riscv_HWPROBE_EXT_ZBB = 0x10
riscv_HWPROBE_EXT_ZBS = 0x20
riscv_HWPROBE_KEY_CPUPERF_0 = 0x5
riscv_HWPROBE_MISALIGNED_FAST = 0x3
riscv_HWPROBE_MISALIGNED_MASK = 0x7
)
const (
// sys_RISCV_HWPROBE is copied from golang.org/x/sys/unix/zsysnum_linux_riscv64.go.
sys_RISCV_HWPROBE = 258
)
// riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go.
type riscvHWProbePairs struct {
key int64
value uint64
}
const (
// CPU features
hwcap_RISCV_ISA_C = 1 << ('C' - 'A')
)
func doinit() {
// A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key
// field should be initialised with one of the key constants defined above, e.g.,
// RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value.
// If the kernel does not recognise a key it will set the key field to -1 and the value field to 0.
pairs := []riscvHWProbePairs{
{riscv_HWPROBE_KEY_IMA_EXT_0, 0},
{riscv_HWPROBE_KEY_CPUPERF_0, 0},
}
// This call only indicates that extensions are supported if they are implemented on all cores.
if riscvHWProbe(pairs, 0) {
if pairs[0].key != -1 {
v := uint(pairs[0].value)
RISCV64.HasC = isSet(v, riscv_HWPROBE_IMA_C)
RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V)
RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA)
RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB)
RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS)
}
if pairs[1].key != -1 {
v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK
RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST
}
}
// Let's double check with HWCAP if the C extension does not appear to be supported.
// This may happen if we're running on a kernel older than 6.4.
if !RISCV64.HasC {
RISCV64.HasC = isSet(hwCap, hwcap_RISCV_ISA_C)
}
}
func isSet(hwc uint, value uint) bool {
return hwc&value != 0
}
// riscvHWProbe is a simplified version of the generated wrapper function found in
// golang.org/x/sys/unix/zsyscall_linux_riscv64.go. We simplify it by removing the
// cpuCount and cpus parameters which we do not need. We always want to pass 0 for
// these parameters here so the kernel only reports the extensions that are present
// on all cores.
func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool {
var _zero uintptr
var p0 unsafe.Pointer
if len(pairs) > 0 {
p0 = unsafe.Pointer(&pairs[0])
} else {
p0 = unsafe.Pointer(&_zero)
}
_, _, e1 := syscall.Syscall6(sys_RISCV_HWPROBE, uintptr(p0), uintptr(len(pairs)), uintptr(0), uintptr(0), uintptr(flags), 0)
return e1 == 0
}

View File

@ -0,0 +1,40 @@
// Copyright 2019 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.
package cpu
const (
// bit mask values from /usr/include/bits/hwcap.h
hwcap_ZARCH = 2
hwcap_STFLE = 4
hwcap_MSA = 8
hwcap_LDISP = 16
hwcap_EIMM = 32
hwcap_DFP = 64
hwcap_ETF3EH = 256
hwcap_VX = 2048
hwcap_VXE = 8192
)
func initS390Xbase() {
// test HWCAP bit vector
has := func(featureMask uint) bool {
return hwCap&featureMask == featureMask
}
// mandatory
S390X.HasZARCH = has(hwcap_ZARCH)
// optional
S390X.HasSTFLE = has(hwcap_STFLE)
S390X.HasLDISP = has(hwcap_LDISP)
S390X.HasEIMM = has(hwcap_EIMM)
S390X.HasETF3EH = has(hwcap_ETF3EH)
S390X.HasDFP = has(hwcap_DFP)
S390X.HasMSA = has(hwcap_MSA)
S390X.HasVX = has(hwcap_VX)
if S390X.HasVX {
S390X.HasVXE = has(hwcap_VXE)
}
}

View File

@ -0,0 +1,12 @@
// Copyright 2022 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 loong64
package cpu
const cacheLineSize = 64
func initOptions() {
}

View File

@ -0,0 +1,15 @@
// Copyright 2018 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 mips64 || mips64le
package cpu
const cacheLineSize = 32
func initOptions() {
options = []option{
{Name: "msa", Feature: &MIPS64X.HasMSA},
}
}

View File

@ -0,0 +1,11 @@
// Copyright 2018 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 mips || mipsle
package cpu
const cacheLineSize = 32
func initOptions() {}

View File

@ -0,0 +1,173 @@
// Copyright 2020 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.
package cpu
import (
"syscall"
"unsafe"
)
// Minimal copy of functionality from x/sys/unix so the cpu package can call
// sysctl without depending on x/sys/unix.
const (
_CTL_QUERY = -2
_SYSCTL_VERS_1 = 0x1000000
)
var _zero uintptr
func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
var _p0 unsafe.Pointer
if len(mib) > 0 {
_p0 = unsafe.Pointer(&mib[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
_, _, errno := syscall.Syscall6(
syscall.SYS___SYSCTL,
uintptr(_p0),
uintptr(len(mib)),
uintptr(unsafe.Pointer(old)),
uintptr(unsafe.Pointer(oldlen)),
uintptr(unsafe.Pointer(new)),
uintptr(newlen))
if errno != 0 {
return errno
}
return nil
}
type sysctlNode struct {
Flags uint32
Num int32
Name [32]int8
Ver uint32
__rsvd uint32
Un [16]byte
_sysctl_size [8]byte
_sysctl_func [8]byte
_sysctl_parent [8]byte
_sysctl_desc [8]byte
}
func sysctlNodes(mib []int32) ([]sysctlNode, error) {
var olen uintptr
// Get a list of all sysctl nodes below the given MIB by performing
// a sysctl for the given MIB with CTL_QUERY appended.
mib = append(mib, _CTL_QUERY)
qnode := sysctlNode{Flags: _SYSCTL_VERS_1}
qp := (*byte)(unsafe.Pointer(&qnode))
sz := unsafe.Sizeof(qnode)
if err := sysctl(mib, nil, &olen, qp, sz); err != nil {
return nil, err
}
// Now that we know the size, get the actual nodes.
nodes := make([]sysctlNode, olen/sz)
np := (*byte)(unsafe.Pointer(&nodes[0]))
if err := sysctl(mib, np, &olen, qp, sz); err != nil {
return nil, err
}
return nodes, nil
}
func nametomib(name string) ([]int32, error) {
// Split name into components.
var parts []string
last := 0
for i := 0; i < len(name); i++ {
if name[i] == '.' {
parts = append(parts, name[last:i])
last = i + 1
}
}
parts = append(parts, name[last:])
mib := []int32{}
// Discover the nodes and construct the MIB OID.
for partno, part := range parts {
nodes, err := sysctlNodes(mib)
if err != nil {
return nil, err
}
for _, node := range nodes {
n := make([]byte, 0)
for i := range node.Name {
if node.Name[i] != 0 {
n = append(n, byte(node.Name[i]))
}
}
if string(n) == part {
mib = append(mib, int32(node.Num))
break
}
}
if len(mib) != partno+1 {
return nil, err
}
}
return mib, nil
}
// aarch64SysctlCPUID is struct aarch64_sysctl_cpu_id from NetBSD's <aarch64/armreg.h>
type aarch64SysctlCPUID struct {
midr uint64 /* Main ID Register */
revidr uint64 /* Revision ID Register */
mpidr uint64 /* Multiprocessor Affinity Register */
aa64dfr0 uint64 /* A64 Debug Feature Register 0 */
aa64dfr1 uint64 /* A64 Debug Feature Register 1 */
aa64isar0 uint64 /* A64 Instruction Set Attribute Register 0 */
aa64isar1 uint64 /* A64 Instruction Set Attribute Register 1 */
aa64mmfr0 uint64 /* A64 Memory Model Feature Register 0 */
aa64mmfr1 uint64 /* A64 Memory Model Feature Register 1 */
aa64mmfr2 uint64 /* A64 Memory Model Feature Register 2 */
aa64pfr0 uint64 /* A64 Processor Feature Register 0 */
aa64pfr1 uint64 /* A64 Processor Feature Register 1 */
aa64zfr0 uint64 /* A64 SVE Feature ID Register 0 */
mvfr0 uint32 /* Media and VFP Feature Register 0 */
mvfr1 uint32 /* Media and VFP Feature Register 1 */
mvfr2 uint32 /* Media and VFP Feature Register 2 */
pad uint32
clidr uint64 /* Cache Level ID Register */
ctr uint64 /* Cache Type Register */
}
func sysctlCPUID(name string) (*aarch64SysctlCPUID, error) {
mib, err := nametomib(name)
if err != nil {
return nil, err
}
out := aarch64SysctlCPUID{}
n := unsafe.Sizeof(out)
_, _, errno := syscall.Syscall6(
syscall.SYS___SYSCTL,
uintptr(unsafe.Pointer(&mib[0])),
uintptr(len(mib)),
uintptr(unsafe.Pointer(&out)),
uintptr(unsafe.Pointer(&n)),
uintptr(0),
uintptr(0))
if errno != 0 {
return nil, errno
}
return &out, nil
}
func doinit() {
cpuid, err := sysctlCPUID("machdep.cpu0.cpu_id")
if err != nil {
setMinimalFeatures()
return
}
parseARM64SystemRegisters(cpuid.aa64isar0, cpuid.aa64isar1, cpuid.aa64pfr0)
Initialized = true
}

View File

@ -0,0 +1,65 @@
// Copyright 2022 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.
package cpu
import (
"syscall"
"unsafe"
)
// Minimal copy of functionality from x/sys/unix so the cpu package can call
// sysctl without depending on x/sys/unix.
const (
// From OpenBSD's sys/sysctl.h.
_CTL_MACHDEP = 7
// From OpenBSD's machine/cpu.h.
_CPU_ID_AA64ISAR0 = 2
_CPU_ID_AA64ISAR1 = 3
)
// Implemented in the runtime package (runtime/sys_openbsd3.go)
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
//go:linkname syscall_syscall6 syscall.syscall6
func sysctl(mib []uint32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
_, _, errno := syscall_syscall6(libc_sysctl_trampoline_addr, uintptr(unsafe.Pointer(&mib[0])), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
if errno != 0 {
return errno
}
return nil
}
var libc_sysctl_trampoline_addr uintptr
//go:cgo_import_dynamic libc_sysctl sysctl "libc.so"
func sysctlUint64(mib []uint32) (uint64, bool) {
var out uint64
nout := unsafe.Sizeof(out)
if err := sysctl(mib, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); err != nil {
return 0, false
}
return out, true
}
func doinit() {
setMinimalFeatures()
// Get ID_AA64ISAR0 and ID_AA64ISAR1 from sysctl.
isar0, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR0})
if !ok {
return
}
isar1, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR1})
if !ok {
return
}
parseARM64SystemRegisters(isar0, isar1, 0)
Initialized = true
}

View File

@ -0,0 +1,11 @@
// Copyright 2022 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.
#include "textflag.h"
TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_sysctl(SB)
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)

View File

@ -0,0 +1,9 @@
// Copyright 2020 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 !linux && arm
package cpu
func archInit() {}

View File

@ -0,0 +1,9 @@
// Copyright 2019 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 !linux && !netbsd && !openbsd && arm64
package cpu
func doinit() {}

View File

@ -0,0 +1,11 @@
// Copyright 2020 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 !linux && (mips64 || mips64le)
package cpu
func archInit() {
Initialized = true
}

View File

@ -0,0 +1,12 @@
// Copyright 2022 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 !aix && !linux && (ppc64 || ppc64le)
package cpu
func archInit() {
PPC64.IsPOWER8 = true
Initialized = true
}

View File

@ -0,0 +1,11 @@
// Copyright 2022 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 !linux && riscv64
package cpu
func archInit() {
Initialized = true
}

View File

@ -0,0 +1,11 @@
// Copyright 2024 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 386 || amd64p32 || (amd64 && (!darwin || !gc))
package cpu
func darwinSupportsAVX512() bool {
panic("only implemented for gc && amd64 && darwin")
}

View File

@ -0,0 +1,16 @@
// Copyright 2020 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 ppc64 || ppc64le
package cpu
const cacheLineSize = 128
func initOptions() {
options = []option{
{Name: "darn", Feature: &PPC64.HasDARN},
{Name: "scv", Feature: &PPC64.HasSCV},
}
}

View File

@ -0,0 +1,20 @@
// Copyright 2019 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 riscv64
package cpu
const cacheLineSize = 64
func initOptions() {
options = []option{
{Name: "fastmisaligned", Feature: &RISCV64.HasFastMisaligned},
{Name: "c", Feature: &RISCV64.HasC},
{Name: "v", Feature: &RISCV64.HasV},
{Name: "zba", Feature: &RISCV64.HasZba},
{Name: "zbb", Feature: &RISCV64.HasZbb},
{Name: "zbs", Feature: &RISCV64.HasZbs},
}
}

View File

@ -0,0 +1,172 @@
// Copyright 2020 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.
package cpu
const cacheLineSize = 256
func initOptions() {
options = []option{
{Name: "zarch", Feature: &S390X.HasZARCH, Required: true},
{Name: "stfle", Feature: &S390X.HasSTFLE, Required: true},
{Name: "ldisp", Feature: &S390X.HasLDISP, Required: true},
{Name: "eimm", Feature: &S390X.HasEIMM, Required: true},
{Name: "dfp", Feature: &S390X.HasDFP},
{Name: "etf3eh", Feature: &S390X.HasETF3EH},
{Name: "msa", Feature: &S390X.HasMSA},
{Name: "aes", Feature: &S390X.HasAES},
{Name: "aescbc", Feature: &S390X.HasAESCBC},
{Name: "aesctr", Feature: &S390X.HasAESCTR},
{Name: "aesgcm", Feature: &S390X.HasAESGCM},
{Name: "ghash", Feature: &S390X.HasGHASH},
{Name: "sha1", Feature: &S390X.HasSHA1},
{Name: "sha256", Feature: &S390X.HasSHA256},
{Name: "sha3", Feature: &S390X.HasSHA3},
{Name: "sha512", Feature: &S390X.HasSHA512},
{Name: "vx", Feature: &S390X.HasVX},
{Name: "vxe", Feature: &S390X.HasVXE},
}
}
// bitIsSet reports whether the bit at index is set. The bit index
// is in big endian order, so bit index 0 is the leftmost bit.
func bitIsSet(bits []uint64, index uint) bool {
return bits[index/64]&((1<<63)>>(index%64)) != 0
}
// facility is a bit index for the named facility.
type facility uint8
const (
// mandatory facilities
zarch facility = 1 // z architecture mode is active
stflef facility = 7 // store-facility-list-extended
ldisp facility = 18 // long-displacement
eimm facility = 21 // extended-immediate
// miscellaneous facilities
dfp facility = 42 // decimal-floating-point
etf3eh facility = 30 // extended-translation 3 enhancement
// cryptography facilities
msa facility = 17 // message-security-assist
msa3 facility = 76 // message-security-assist extension 3
msa4 facility = 77 // message-security-assist extension 4
msa5 facility = 57 // message-security-assist extension 5
msa8 facility = 146 // message-security-assist extension 8
msa9 facility = 155 // message-security-assist extension 9
// vector facilities
vx facility = 129 // vector facility
vxe facility = 135 // vector-enhancements 1
vxe2 facility = 148 // vector-enhancements 2
)
// facilityList contains the result of an STFLE call.
// Bits are numbered in big endian order so the
// leftmost bit (the MSB) is at index 0.
type facilityList struct {
bits [4]uint64
}
// Has reports whether the given facilities are present.
func (s *facilityList) Has(fs ...facility) bool {
if len(fs) == 0 {
panic("no facility bits provided")
}
for _, f := range fs {
if !bitIsSet(s.bits[:], uint(f)) {
return false
}
}
return true
}
// function is the code for the named cryptographic function.
type function uint8
const (
// KM{,A,C,CTR} function codes
aes128 function = 18 // AES-128
aes192 function = 19 // AES-192
aes256 function = 20 // AES-256
// K{I,L}MD function codes
sha1 function = 1 // SHA-1
sha256 function = 2 // SHA-256
sha512 function = 3 // SHA-512
sha3_224 function = 32 // SHA3-224
sha3_256 function = 33 // SHA3-256
sha3_384 function = 34 // SHA3-384
sha3_512 function = 35 // SHA3-512
shake128 function = 36 // SHAKE-128
shake256 function = 37 // SHAKE-256
// KLMD function codes
ghash function = 65 // GHASH
)
// queryResult contains the result of a Query function
// call. Bits are numbered in big endian order so the
// leftmost bit (the MSB) is at index 0.
type queryResult struct {
bits [2]uint64
}
// Has reports whether the given functions are present.
func (q *queryResult) Has(fns ...function) bool {
if len(fns) == 0 {
panic("no function codes provided")
}
for _, f := range fns {
if !bitIsSet(q.bits[:], uint(f)) {
return false
}
}
return true
}
func doinit() {
initS390Xbase()
// We need implementations of stfle, km and so on
// to detect cryptographic features.
if !haveAsmFunctions() {
return
}
// optional cryptographic functions
if S390X.HasMSA {
aes := []function{aes128, aes192, aes256}
// cipher message
km, kmc := kmQuery(), kmcQuery()
S390X.HasAES = km.Has(aes...)
S390X.HasAESCBC = kmc.Has(aes...)
if S390X.HasSTFLE {
facilities := stfle()
if facilities.Has(msa4) {
kmctr := kmctrQuery()
S390X.HasAESCTR = kmctr.Has(aes...)
}
if facilities.Has(msa8) {
kma := kmaQuery()
S390X.HasAESGCM = kma.Has(aes...)
}
}
// compute message digest
kimd := kimdQuery() // intermediate (no padding)
klmd := klmdQuery() // last (padding)
S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1)
S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256)
S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512)
S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist
sha3 := []function{
sha3_224, sha3_256, sha3_384, sha3_512,
shake128, shake256,
}
S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...)
}
}

View File

@ -0,0 +1,57 @@
// Copyright 2019 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 gc
#include "textflag.h"
// func stfle() facilityList
TEXT ·stfle(SB), NOSPLIT|NOFRAME, $0-32
MOVD $ret+0(FP), R1
MOVD $3, R0 // last doubleword index to store
XC $32, (R1), (R1) // clear 4 doublewords (32 bytes)
WORD $0xb2b01000 // store facility list extended (STFLE)
RET
// func kmQuery() queryResult
TEXT ·kmQuery(SB), NOSPLIT|NOFRAME, $0-16
MOVD $0, R0 // set function code to 0 (KM-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB92E0024 // cipher message (KM)
RET
// func kmcQuery() queryResult
TEXT ·kmcQuery(SB), NOSPLIT|NOFRAME, $0-16
MOVD $0, R0 // set function code to 0 (KMC-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB92F0024 // cipher message with chaining (KMC)
RET
// func kmctrQuery() queryResult
TEXT ·kmctrQuery(SB), NOSPLIT|NOFRAME, $0-16
MOVD $0, R0 // set function code to 0 (KMCTR-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB92D4024 // cipher message with counter (KMCTR)
RET
// func kmaQuery() queryResult
TEXT ·kmaQuery(SB), NOSPLIT|NOFRAME, $0-16
MOVD $0, R0 // set function code to 0 (KMA-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xb9296024 // cipher message with authentication (KMA)
RET
// func kimdQuery() queryResult
TEXT ·kimdQuery(SB), NOSPLIT|NOFRAME, $0-16
MOVD $0, R0 // set function code to 0 (KIMD-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB93E0024 // compute intermediate message digest (KIMD)
RET
// func klmdQuery() queryResult
TEXT ·klmdQuery(SB), NOSPLIT|NOFRAME, $0-16
MOVD $0, R0 // set function code to 0 (KLMD-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB93F0024 // compute last message digest (KLMD)
RET

View File

@ -0,0 +1,75 @@
// Copyright 2020 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.
package cpu_test
import (
"runtime"
"testing"
"unsafe"
"github.com/emmansun/gmsm/internal/deps/cpu"
)
var s390xTests = []struct {
name string
feature bool
facility uint
mandatory bool
}{
{"ZARCH", cpu.S390X.HasZARCH, 1, true},
{"STFLE", cpu.S390X.HasSTFLE, 7, true},
{"LDISP", cpu.S390X.HasLDISP, 18, true},
{"EIMM", cpu.S390X.HasEIMM, 21, true},
{"DFP", cpu.S390X.HasDFP, 42, false},
{"MSA", cpu.S390X.HasMSA, 17, false},
{"VX", cpu.S390X.HasVX, 129, false},
{"VXE", cpu.S390X.HasVXE, 135, false},
}
// bitIsSet reports whether the bit at index is set. The bit index
// is in big endian order, so bit index 0 is the leftmost bit.
func bitIsSet(bits [4]uint64, i uint) bool {
return bits[i/64]&((1<<63)>>(i%64)) != 0
}
// facilityList contains the contents of location 200 on zos.
// Bits are numbered in big endian order so the
// leftmost bit (the MSB) is at index 0.
type facilityList struct {
bits [4]uint64
}
func TestS390XVectorFacilityFeatures(t *testing.T) {
// vector-enhancements require vector facility to be enabled
if cpu.S390X.HasVXE && !cpu.S390X.HasVX {
t.Error("HasVX expected true, got false (VXE is true)")
}
}
func TestS390XMandatoryFeatures(t *testing.T) {
for _, tc := range s390xTests {
if tc.mandatory && !tc.feature {
t.Errorf("Feature %s is mandatory but is not present", tc.name)
}
}
}
func TestS390XFeatures(t *testing.T) {
if runtime.GOOS != "zos" {
return
}
// Read available facilities from address 200.
facilitiesAddress := uintptr(200)
var facilities facilityList
for i := 0; i < 4; i++ {
facilities.bits[i] = *(*uint64)(unsafe.Pointer(facilitiesAddress + uintptr(8*i)))
}
for _, tc := range s390xTests {
if want := bitIsSet(facilities.bits, tc.facility); want != tc.feature {
t.Errorf("Feature %s expected %v, got %v", tc.name, want, tc.feature)
}
}
}

View File

@ -0,0 +1,17 @@
// Copyright 2019 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 wasm
package cpu
// We're compiling the cpu package for an unknown (software-abstracted) CPU.
// Make CacheLinePad an empty struct and hope that the usual struct alignment
// rules are good enough.
const cacheLineSize = 0
func initOptions() {}
func archInit() {}

View File

@ -0,0 +1,162 @@
// Copyright 2018 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 386 || amd64 || amd64p32
package cpu
import "runtime"
const cacheLineSize = 64
func initOptions() {
options = []option{
{Name: "adx", Feature: &X86.HasADX},
{Name: "aes", Feature: &X86.HasAES},
{Name: "avx", Feature: &X86.HasAVX},
{Name: "avx2", Feature: &X86.HasAVX2},
{Name: "avx512", Feature: &X86.HasAVX512},
{Name: "avx512f", Feature: &X86.HasAVX512F},
{Name: "avx512cd", Feature: &X86.HasAVX512CD},
{Name: "avx512er", Feature: &X86.HasAVX512ER},
{Name: "avx512pf", Feature: &X86.HasAVX512PF},
{Name: "avx512vl", Feature: &X86.HasAVX512VL},
{Name: "avx512bw", Feature: &X86.HasAVX512BW},
{Name: "avx512dq", Feature: &X86.HasAVX512DQ},
{Name: "avx512ifma", Feature: &X86.HasAVX512IFMA},
{Name: "avx512vbmi", Feature: &X86.HasAVX512VBMI},
{Name: "avx512vnniw", Feature: &X86.HasAVX5124VNNIW},
{Name: "avx5124fmaps", Feature: &X86.HasAVX5124FMAPS},
{Name: "avx512vpopcntdq", Feature: &X86.HasAVX512VPOPCNTDQ},
{Name: "avx512vpclmulqdq", Feature: &X86.HasAVX512VPCLMULQDQ},
{Name: "avx512vnni", Feature: &X86.HasAVX512VNNI},
{Name: "avx512gfni", Feature: &X86.HasAVX512GFNI},
{Name: "avx512vaes", Feature: &X86.HasAVX512VAES},
{Name: "avx512vbmi2", Feature: &X86.HasAVX512VBMI2},
{Name: "avx512bitalg", Feature: &X86.HasAVX512BITALG},
{Name: "avx512bf16", Feature: &X86.HasAVX512BF16},
{Name: "amxtile", Feature: &X86.HasAMXTile},
{Name: "amxint8", Feature: &X86.HasAMXInt8},
{Name: "amxbf16", Feature: &X86.HasAMXBF16},
{Name: "bmi1", Feature: &X86.HasBMI1},
{Name: "bmi2", Feature: &X86.HasBMI2},
{Name: "cx16", Feature: &X86.HasCX16},
{Name: "erms", Feature: &X86.HasERMS},
{Name: "fma", Feature: &X86.HasFMA},
{Name: "osxsave", Feature: &X86.HasOSXSAVE},
{Name: "pclmulqdq", Feature: &X86.HasPCLMULQDQ},
{Name: "popcnt", Feature: &X86.HasPOPCNT},
{Name: "rdrand", Feature: &X86.HasRDRAND},
{Name: "rdseed", Feature: &X86.HasRDSEED},
{Name: "sse3", Feature: &X86.HasSSE3},
{Name: "sse41", Feature: &X86.HasSSE41},
{Name: "sse42", Feature: &X86.HasSSE42},
{Name: "ssse3", Feature: &X86.HasSSSE3},
{Name: "avxifma", Feature: &X86.HasAVXIFMA},
{Name: "avxvnni", Feature: &X86.HasAVXVNNI},
{Name: "avxvnniint8", Feature: &X86.HasAVXVNNIInt8},
// These capabilities should always be enabled on amd64:
{Name: "sse2", Feature: &X86.HasSSE2, Required: runtime.GOARCH == "amd64"},
}
}
func archInit() {
Initialized = true
maxID, _, _, _ := cpuid(0, 0)
if maxID < 1 {
return
}
_, _, ecx1, edx1 := cpuid(1, 0)
X86.HasSSE2 = isSet(26, edx1)
X86.HasSSE3 = isSet(0, ecx1) // Check presence of SSE3 - bit 0 of ECX
X86.HasPCLMULQDQ = isSet(1, ecx1) // Check presence of PCLMULQDQ - bit 1 of ECX
X86.HasSSSE3 = isSet(9, ecx1) // Check presence of SSSE3 - bit 9 of ECX
X86.HasFMA = isSet(12, ecx1) // Check presence of FMA - bit 12 of ECX
X86.HasCX16 = isSet(13, ecx1) // Check presence of CX16 - bit 13 of ECX
X86.HasSSE41 = isSet(19, ecx1) // Check presence of SSE4.1 - bit 19 of ECX
X86.HasSSE42 = isSet(20, ecx1) // Check presence of SSE4.2 - bit 20 of ECX
X86.HasPOPCNT = isSet(23, ecx1) // Check presence of POPCNT - bit 23 of ECX
X86.HasAES = isSet(25, ecx1) // Check presence of AESNI - bit 25 of ECX
X86.HasOSXSAVE = isSet(27, ecx1) // Check presence of OSXSAVE - bit 27 of ECX
X86.HasRDRAND = isSet(30, ecx1) // Check presence of RDRAND - bit 30 of ECX
var osSupportsAVX, osSupportsAVX512 bool
// For XGETBV, OSXSAVE bit is required and sufficient.
if X86.HasOSXSAVE {
eax, _ := xgetbv()
// Check if XMM and YMM registers have OS support.
osSupportsAVX = isSet(1, eax) && isSet(2, eax)
if runtime.GOOS == "darwin" {
// Darwin requires special AVX512 checks, see cpu_darwin_x86.go
osSupportsAVX512 = osSupportsAVX && darwinSupportsAVX512()
} else {
// Check if OPMASK and ZMM registers have OS support.
osSupportsAVX512 = osSupportsAVX && isSet(5, eax) && isSet(6, eax) && isSet(7, eax)
}
}
X86.HasAVX = isSet(28, ecx1) && osSupportsAVX
if maxID < 7 {
return
}
eax7, ebx7, ecx7, edx7 := cpuid(7, 0)
X86.HasBMI1 = isSet(3, ebx7) // Check presence of BMI1 - bit 3 of EBX
X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX // Check presence of AVX2 - bit 5 of EBX
X86.HasBMI2 = isSet(8, ebx7) // Check presence of BMI2 - bit 8 of EBX
X86.HasERMS = isSet(9, ebx7)
X86.HasRDSEED = isSet(18, ebx7)
X86.HasADX = isSet(19, ebx7)
X86.HasAVX512 = isSet(16, ebx7) && osSupportsAVX512 // Because avx-512 foundation is the core required extension
if X86.HasAVX512 {
X86.HasAVX512F = true
X86.HasAVX512CD = isSet(28, ebx7) // Check presence of AVX512CD - bit 28 of EBX
X86.HasAVX512ER = isSet(27, ebx7) // Check presence of AVX512ER - bit 27 of EBX
X86.HasAVX512PF = isSet(26, ebx7) // Check presence of AVX512PF - bit 26 of EBX
X86.HasAVX512VL = isSet(31, ebx7) // Check presence of AVX512VL - bit 31 of EBX
X86.HasAVX512BW = isSet(30, ebx7) // Check presence of AVX512BW - bit 30 of EBX
X86.HasAVX512DQ = isSet(17, ebx7) // Check presence of AVX512F - bit 16 of EBX
X86.HasAVX512IFMA = isSet(21, ebx7) // Check presence of AVX512IFMA - bit 21 of EBX
X86.HasAVX512VBMI = isSet(1, ecx7) // Check presence of AVX512VBMI - bit 1 of ECX
X86.HasAVX5124VNNIW = isSet(2, edx7) // Check presence of AVX5124VNNIW - bit 2 of EDX
X86.HasAVX5124FMAPS = isSet(3, edx7) // Check presence of AVX5124FMAPS - bit 3 of EDX
X86.HasAVX512VPOPCNTDQ = isSet(14, ecx7) // Check presence of AVX512VPOPCNTDQ - bit 14 of ECX
X86.HasAVX512VPCLMULQDQ = isSet(10, ecx7) // Check presence of VPCLMULQDQ - bit 10 of ECX
X86.HasAVX512VNNI = isSet(11, ecx7) // Check presence of AVX512VNNI - bit 11 of ECX
X86.HasAVX512GFNI = isSet(8, ecx7) // Check presence of AVX512GFNI - bit 8 of ECX
X86.HasAVX512VAES = isSet(9, ecx7) // Check presence of AVX512VAES - bit 9 of ECX
X86.HasAVX512VBMI2 = isSet(6, ecx7) // Check presence of AVX512VBMI2 - bit 6 of ECX
X86.HasAVX512BITALG = isSet(12, ecx7) // Check presence of AVX512BITALG - bit 12 of ECX
}
X86.HasAMXTile = isSet(24, edx7)
X86.HasAMXInt8 = isSet(25, edx7)
X86.HasAMXBF16 = isSet(22, edx7)
// These features depend on the second level of extended features.
if eax7 >= 1 {
eax71, _, _, edx71 := cpuid(7, 1)
if X86.HasAVX512 {
X86.HasAVX512BF16 = isSet(5, eax71)
}
if X86.HasAVX {
X86.HasAVXIFMA = isSet(23, eax71) // Check presence of AVXIFMA - bit 23 of EAX
X86.HasAVXVNNI = isSet(4, eax71)
X86.HasAVXVNNIInt8 = isSet(4, edx71)
}
}
}
func isSet(bitpos uint, value uint32) bool {
return value&(1<<bitpos) != 0
}

View File

@ -0,0 +1,37 @@
// Copyright 2018 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 386 || amd64
package cpu_test
import (
"testing"
. "github.com/emmansun/gmsm/internal/deps/cpu"
)
func TestX86ifAVX2hasAVX(t *testing.T) {
if X86.HasAVX2 && !X86.HasAVX {
t.Fatalf("HasAVX expected true when HasAVX2 is true, got false")
}
}
func TestX86ifAVX512FhasAVX2(t *testing.T) {
if X86.HasAVX512F && !X86.HasAVX2 {
t.Fatalf("HasAVX2 expected true when HasAVX512F is true, got false")
}
}
func TestX86ifAVX512BWhasAVX512F(t *testing.T) {
if X86.HasAVX512BW && !X86.HasAVX512F {
t.Fatalf("HasAVX512F expected true when HasAVX512BW is true, got false")
}
}
func TestX86ifAVX512VLhasAVX512F(t *testing.T) {
if X86.HasAVX512VL && !X86.HasAVX512F {
t.Fatalf("HasAVX512F expected true when HasAVX512VL is true, got false")
}
}

View File

@ -0,0 +1,10 @@
// Copyright 2020 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.
package cpu
func archInit() {
doinit()
Initialized = true
}

View File

@ -0,0 +1,25 @@
// Copyright 2020 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.
package cpu
func initS390Xbase() {
// get the facilities list
facilities := stfle()
// mandatory
S390X.HasZARCH = facilities.Has(zarch)
S390X.HasSTFLE = facilities.Has(stflef)
S390X.HasLDISP = facilities.Has(ldisp)
S390X.HasEIMM = facilities.Has(eimm)
// optional
S390X.HasETF3EH = facilities.Has(etf3eh)
S390X.HasDFP = facilities.Has(dfp)
S390X.HasMSA = facilities.Has(msa)
S390X.HasVX = facilities.Has(vx)
if S390X.HasVX {
S390X.HasVXE = facilities.Has(vxe)
}
}

View File

@ -0,0 +1,10 @@
// Copyright 2023 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 armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64
package cpu
// IsBigEndian records whether the GOARCH's byte order is big endian.
const IsBigEndian = true

View File

@ -0,0 +1,10 @@
// Copyright 2023 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 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh || wasm
package cpu
// IsBigEndian records whether the GOARCH's byte order is big endian.
const IsBigEndian = false

View File

@ -0,0 +1,21 @@
// Copyright 2023 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.
package cpu_test
import (
"testing"
"unsafe"
"github.com/emmansun/gmsm/internal/deps/cpu"
)
func TestIsBigEndian(t *testing.T) {
b := uint16(0xff00)
want := *(*byte)(unsafe.Pointer(&b)) == 0xff
if cpu.IsBigEndian != want {
t.Errorf("IsBigEndian = %t, want %t",
cpu.IsBigEndian, want)
}
}

View File

@ -0,0 +1,71 @@
// Copyright 2019 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.
package cpu
import (
"os"
)
const (
_AT_HWCAP = 16
_AT_HWCAP2 = 26
procAuxv = "/proc/self/auxv"
uintSize = int(32 << (^uint(0) >> 63))
)
// For those platforms don't have a 'cpuid' equivalent we use HWCAP/HWCAP2
// These are initialized in cpu_$GOARCH.go
// and should not be changed after they are initialized.
var hwCap uint
var hwCap2 uint
func readHWCAP() error {
// For Go 1.21+, get auxv from the Go runtime.
if a := getAuxv(); len(a) > 0 {
for len(a) >= 2 {
tag, val := a[0], uint(a[1])
a = a[2:]
switch tag {
case _AT_HWCAP:
hwCap = val
case _AT_HWCAP2:
hwCap2 = val
}
}
return nil
}
buf, err := os.ReadFile(procAuxv)
if err != nil {
// e.g. on android /proc/self/auxv is not accessible, so silently
// ignore the error and leave Initialized = false. On some
// architectures (e.g. arm64) doinit() implements a fallback
// readout and will set Initialized = true again.
return err
}
bo := hostByteOrder()
for len(buf) >= 2*(uintSize/8) {
var tag, val uint
switch uintSize {
case 32:
tag = uint(bo.Uint32(buf[0:]))
val = uint(bo.Uint32(buf[4:]))
buf = buf[8:]
case 64:
tag = uint(bo.Uint64(buf[0:]))
val = uint(bo.Uint64(buf[8:]))
buf = buf[16:]
}
switch tag {
case _AT_HWCAP:
hwCap = val
case _AT_HWCAP2:
hwCap2 = val
}
}
return nil
}

View File

@ -0,0 +1,43 @@
// Copyright 2022 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.
package cpu
import "strconv"
// parseRelease parses a dot-separated version number. It follows the semver
// syntax, but allows the minor and patch versions to be elided.
//
// This is a copy of the Go runtime's parseRelease from
// https://golang.org/cl/209597.
func parseRelease(rel string) (major, minor, patch int, ok bool) {
// Strip anything after a dash or plus.
for i := 0; i < len(rel); i++ {
if rel[i] == '-' || rel[i] == '+' {
rel = rel[:i]
break
}
}
next := func() (int, bool) {
for i := 0; i < len(rel); i++ {
if rel[i] == '.' {
ver, err := strconv.Atoi(rel[:i])
rel = rel[i+1:]
return ver, err == nil
}
}
ver, err := strconv.Atoi(rel)
rel = ""
return ver, err == nil
}
if major, ok = next(); !ok || rel == "" {
return
}
if minor, ok = next(); !ok || rel == "" {
return
}
patch, ok = next()
return
}

View File

@ -0,0 +1,38 @@
// Copyright 2022 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.
package cpu
import "testing"
type parseReleaseTest struct {
in string
major, minor, patch int
}
var parseReleaseTests = []parseReleaseTest{
{"", -1, -1, -1},
{"x", -1, -1, -1},
{"5", 5, 0, 0},
{"5.12", 5, 12, 0},
{"5.12-x", 5, 12, 0},
{"5.12.1", 5, 12, 1},
{"5.12.1-x", 5, 12, 1},
{"5.12.1.0", 5, 12, 1},
{"5.20496382327982653440", -1, -1, -1},
}
func TestParseRelease(t *testing.T) {
for _, test := range parseReleaseTests {
major, minor, patch, ok := parseRelease(test.in)
if !ok {
major, minor, patch = -1, -1, -1
}
if test.major != major || test.minor != minor || test.patch != patch {
t.Errorf("parseRelease(%q) = (%v, %v, %v) want (%v, %v, %v)",
test.in, major, minor, patch,
test.major, test.minor, test.patch)
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright 2022 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 linux && arm64
package cpu
import (
"errors"
"io"
"os"
"strings"
)
func readLinuxProcCPUInfo() error {
f, err := os.Open("/proc/cpuinfo")
if err != nil {
return err
}
defer f.Close()
var buf [1 << 10]byte // enough for first CPU
n, err := io.ReadFull(f, buf[:])
if err != nil && err != io.ErrUnexpectedEOF {
return err
}
in := string(buf[:n])
const features = "\nFeatures : "
i := strings.Index(in, features)
if i == -1 {
return errors.New("no CPU features found")
}
in = in[i+len(features):]
if i := strings.Index(in, "\n"); i != -1 {
in = in[:i]
}
m := map[string]*bool{}
initOptions() // need it early here; it's harmless to call twice
for _, o := range options {
m[o.Name] = o.Feature
}
// The EVTSTRM field has alias "evstrm" in Go, but Linux calls it "evtstrm".
m["evtstrm"] = &ARM64.HasEVTSTRM
for _, f := range strings.Fields(in) {
if p, ok := m[f]; ok {
*p = true
}
}
return nil
}

View File

@ -0,0 +1,16 @@
// Copyright 2023 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.
package cpu
// getAuxvFn is non-nil on Go 1.21+ (via runtime_auxv_go121.go init)
// on platforms that use auxv.
var getAuxvFn func() []uintptr
func getAuxv() []uintptr {
if getAuxvFn == nil {
return nil
}
return getAuxvFn()
}

View File

@ -0,0 +1,18 @@
// Copyright 2023 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 go1.21
package cpu
import (
_ "unsafe" // for linkname
)
//go:linkname runtime_getAuxv runtime.getAuxv
func runtime_getAuxv() []uintptr
func init() {
getAuxvFn = runtime_getAuxv
}

View File

@ -0,0 +1,20 @@
// Copyright 2023 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 go1.21
package cpu
import (
"runtime"
"testing"
)
func TestAuxvFromRuntime(t *testing.T) {
got := getAuxv()
t.Logf("got: %v", got) // notably: we didn't panic
if runtime.GOOS == "linux" && len(got) == 0 {
t.Errorf("expected auxv on linux; got zero entries")
}
}

View File

@ -0,0 +1,26 @@
// Copyright 2020 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.
// Recreate a getsystemcfg syscall handler instead of
// using the one provided by x/sys/unix to avoid having
// the dependency between them. (See golang.org/issue/32102)
// Moreover, this file will be used during the building of
// gccgo's libgo and thus must not used a CGo method.
//go:build aix && gccgo
package cpu
import (
"syscall"
)
//extern getsystemcfg
func gccgoGetsystemcfg(label uint32) (r uint64)
func callgetsystemcfg(label int) (r1 uintptr, e1 syscall.Errno) {
r1 = uintptr(gccgoGetsystemcfg(uint32(label)))
e1 = syscall.GetErrno()
return
}

View File

@ -0,0 +1,35 @@
// Copyright 2019 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.
// Minimal copy of x/sys/unix so the cpu package can make a
// system call on AIX without depending on x/sys/unix.
// (See golang.org/issue/32102)
//go:build aix && ppc64 && gc
package cpu
import (
"syscall"
"unsafe"
)
//go:cgo_import_dynamic libc_getsystemcfg getsystemcfg "libc.a/shr_64.o"
//go:linkname libc_getsystemcfg libc_getsystemcfg
type syscallFunc uintptr
var libc_getsystemcfg syscallFunc
type errno = syscall.Errno
// Implemented in runtime/syscall_aix.go.
func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err errno)
func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err errno)
func callgetsystemcfg(label int) (r1 uintptr, e1 errno) {
r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_getsystemcfg)), 1, uintptr(label), 0, 0, 0, 0, 0)
return
}

View File

@ -0,0 +1,98 @@
// Copyright 2024 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.
// Minimal copy of x/sys/unix so the cpu package can make a
// system call on Darwin without depending on x/sys/unix.
//go:build darwin && amd64 && gc
package cpu
import (
"syscall"
"unsafe"
)
type _C_int int32
// adapted from unix.Uname() at x/sys/unix/syscall_darwin.go L419
func darwinOSRelease(release *[256]byte) error {
// from x/sys/unix/zerrors_openbsd_amd64.go
const (
CTL_KERN = 0x1
KERN_OSRELEASE = 0x2
)
mib := []_C_int{CTL_KERN, KERN_OSRELEASE}
n := unsafe.Sizeof(*release)
return sysctl(mib, &release[0], &n, nil, 0)
}
type Errno = syscall.Errno
var _zero uintptr // Single-word zero for use when we need a valid pointer to 0 bytes.
// from x/sys/unix/zsyscall_darwin_amd64.go L791-807
func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error {
var _p0 unsafe.Pointer
if len(mib) > 0 {
_p0 = unsafe.Pointer(&mib[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
if _, _, err := syscall_syscall6(
libc_sysctl_trampoline_addr,
uintptr(_p0),
uintptr(len(mib)),
uintptr(unsafe.Pointer(old)),
uintptr(unsafe.Pointer(oldlen)),
uintptr(unsafe.Pointer(new)),
uintptr(newlen),
); err != 0 {
return err
}
return nil
}
var libc_sysctl_trampoline_addr uintptr
// adapted from internal/cpu/cpu_arm64_darwin.go
func darwinSysctlEnabled(name []byte) bool {
out := int32(0)
nout := unsafe.Sizeof(out)
if ret := sysctlbyname(&name[0], (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); ret != nil {
return false
}
return out > 0
}
//go:cgo_import_dynamic libc_sysctl sysctl "/usr/lib/libSystem.B.dylib"
var libc_sysctlbyname_trampoline_addr uintptr
// adapted from runtime/sys_darwin.go in the pattern of sysctl() above, as defined in x/sys/unix
func sysctlbyname(name *byte, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error {
if _, _, err := syscall_syscall6(
libc_sysctlbyname_trampoline_addr,
uintptr(unsafe.Pointer(name)),
uintptr(unsafe.Pointer(old)),
uintptr(unsafe.Pointer(oldlen)),
uintptr(unsafe.Pointer(new)),
uintptr(newlen),
0,
); err != 0 {
return err
}
return nil
}
//go:cgo_import_dynamic libc_sysctlbyname sysctlbyname "/usr/lib/libSystem.B.dylib"
// Implemented in the runtime package (runtime/sys_darwin.go)
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
//go:linkname syscall_syscall6 syscall.syscall6

Some files were not shown because too many files have changed in this diff Show More