Compare commits

...

103 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
Sun Yimin
33c609242c
internal/cpuid: use var block 2025-02-26 08:25:58 +08:00
Sun Yimin
d2dd4de947
doc: add smart questions link 2025-02-18 08:49:36 +08:00
Sun Yimin
1aed89febd
subtle: combine xor_<goarch>.go files #304 2025-02-17 08:32:20 +08:00
Sun Yimin
a248fea7f9
Merge pull request #302 from emmansun/dependabot/go_modules/golang.org/x/crypto-0.33.0
build(deps): bump golang.org/x/crypto from 0.32.0 to 0.33.0
2025-02-11 07:34:43 +08:00
dependabot[bot]
53465ae10a
build(deps): bump golang.org/x/crypto from 0.32.0 to 0.33.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.32.0 to 0.33.0.
- [Commits](https://github.com/golang/crypto/compare/v0.32.0...v0.33.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-02-10 23:30:00 +00:00
Sun Yimin
ea592fe3d0
cfca: fix typo 2025-01-23 16:35:32 +08:00
Sun Yimin
9e57bb91b2
cfca: add examples 2025-01-23 11:28:54 +08:00
Sun Yimin
ee55d370f8
improve test coverage and remove deprecated methods 2025-01-22 10:28:01 +08:00
Sun Yimin
4c88efbaee
sm2: provide a specific hash.Hash implementation #297 2025-01-20 17:36:46 +08:00
Sun Yimin
80a264c995
sm2: update document 2025-01-17 13:54:16 +08:00
Sun Yimin
50a5e49d38
smx509: properly check for IPv6 hosts in URIs #296 2025-01-17 08:30:27 +08:00
Sun Yimin
cb51b3657a
pkcs7: sync with upstream 2025-01-16 16:04:45 +08:00
Sun Yimin
cf6e203d06
pkcs7: update comments and document 2025-01-16 10:59:30 +08:00
Sun Yimin
057ddb5bcb
pkcs7: fix error message 2025-01-15 17:18:37 +08:00
Sun Yimin
94e533ca15
pkcs7: fix typo #294 2025-01-15 08:50:09 +08:00
Sun Yimin
818e14ee32
pkcs7: fix verify signing time check issue #294 2025-01-14 17:10:00 +08:00
Sun Yimin
8331b37e37
pkcs7: deleted a line by mistake #294 2025-01-14 16:52:15 +08:00
Sun Yimin
bef7f5421e
pkcs7: sign precomputed digest #294 2025-01-14 16:41:03 +08:00
Sun Yimin
5a0ff81dc3
Merge pull request #293 from emmansun/dependabot/go_modules/golang.org/x/crypto-0.32.0
build(deps): bump golang.org/x/crypto from 0.31.0 to 0.32.0
2025-01-07 07:53:00 +08:00
dependabot[bot]
b865b59a95
build(deps): bump golang.org/x/crypto from 0.31.0 to 0.32.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.31.0 to 0.32.0.
- [Commits](https://github.com/golang/crypto/compare/v0.31.0...v0.32.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-01-06 23:48:52 +00:00
Sun Yimin
0a961f152c
Merge pull request #292 from emmansun/dependabot/go_modules/golang.org/x/sys-0.29.0
build(deps): bump golang.org/x/sys from 0.28.0 to 0.29.0
2025-01-07 07:47:51 +08:00
dependabot[bot]
3544c09801
build(deps): bump golang.org/x/sys from 0.28.0 to 0.29.0
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.28.0 to 0.29.0.
- [Commits](https://github.com/golang/sys/compare/v0.28.0...v0.29.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-06 23:43:07 +00:00
Sun Yimin
e5652b6638
smx509: add wasip1 support #290 2024-12-27 08:07:33 +08:00
Sun Yimin
309e920050
doc: update sm2.md 2024-12-20 17:57:35 +08:00
Sun Yimin
805fa40c97
pkcs7: code review and refactor 2024-12-20 08:34:32 +08:00
Sun Yimin
d199697b20
pkcs7: remove deprecated method SignWithSM2 2024-12-19 15:42:19 +08:00
Sun Yimin
6b18205bba
sm2: fix test cases 2024-12-19 08:28:00 +08:00
Sun Yimin
89317b8f0b
sm2: code review and refactor 2024-12-19 08:17:21 +08:00
Sun Yimin
a71e806a2d
sm9: code review 2024-12-17 18:06:15 +08:00
Sun Yimin
a53c8b97f8
Merge pull request #289 from emmansun/dependabot/go_modules/golang.org/x/crypto-0.31.0
build(deps): bump golang.org/x/crypto from 0.30.0 to 0.31.0
2024-12-17 07:28:08 +08:00
dependabot[bot]
4fd9003243
build(deps): bump golang.org/x/crypto from 0.30.0 to 0.31.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.30.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.30.0...v0.31.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>
2024-12-16 23:17:20 +00:00
Sun Yimin
0d3a2d2d18
cfca: pkcs12 sm2, supplement test data 2024-12-16 12:54:36 +08:00
Sun Yimin
008b209765
cfca: pkcs7 test, avoid random failure 2024-12-13 18:16:08 +08:00
Sun Yimin
ab670f8ced
cfca: pkcs7 test, avoid random failure 2024-12-13 18:14:06 +08:00
Sun Yimin
8e02380f47
update README 2024-12-13 18:07:41 +08:00
344 changed files with 13479 additions and 7961 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

@ -23,6 +23,8 @@ Go语言商用密码软件简称**GMSM**,一个安全、高性能、易于
- [PKCS7应用指南](./docs/pkcs7.md) - [PKCS7应用指南](./docs/pkcs7.md)
- [PKCS12应用指南](./docs/pkcs12.md) - [PKCS12应用指南](./docs/pkcs12.md)
如果你想提问题,建议你阅读[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md)。
## 包结构 ## 包结构
- **SM2** - SM2椭圆曲线公钥密码算法曲线的具体实现位于[internal/sm2ec](https://github.com/emmansun/gmsm/tree/main/internal/sm2ec) package中。SM2曲线实现性能和Golang标准库中的NIST P256椭圆曲线原生实现非BoringCrypto类似也对**amd64****arm64****s390x**和**ppc64le**架构做了专门汇编优化实现,您也可以参考[SM2实现细节](https://github.com/emmansun/gmsm/wiki/SM2%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96)及相关Wiki和代码以获得更多实现细节。SM2包实现了SM2椭圆曲线公钥密码算法的数字签名算法、公钥加密算法、密钥交换算法以及《GB/T 35276-2017信息安全技术 SM2密码算法使用规范》中的密钥对保护数据格式。 - **SM2** - SM2椭圆曲线公钥密码算法曲线的具体实现位于[internal/sm2ec](https://github.com/emmansun/gmsm/tree/main/internal/sm2ec) package中。SM2曲线实现性能和Golang标准库中的NIST P256椭圆曲线原生实现非BoringCrypto类似也对**amd64****arm64****s390x**和**ppc64le**架构做了专门汇编优化实现,您也可以参考[SM2实现细节](https://github.com/emmansun/gmsm/wiki/SM2%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96)及相关Wiki和代码以获得更多实现细节。SM2包实现了SM2椭圆曲线公钥密码算法的数字签名算法、公钥加密算法、密钥交换算法以及《GB/T 35276-2017信息安全技术 SM2密码算法使用规范》中的密钥对保护数据格式。
@ -35,13 +37,13 @@ Go语言商用密码软件简称**GMSM**,一个安全、高性能、易于
- **ZUC** - 祖冲之序列密码算法实现。使用SIMD、AES指令以及无进位乘法指令分别对**amd64**、**arm64**和**ppc64x**架构做了优化实现, 您也可以参考[ZUC实现及优化](https://github.com/emmansun/gmsm/wiki/Efficient-Software-Implementations-of-ZUC)和相关代码以获得更多实现细节。ZUC包实现了基于祖冲之序列密码算法的机密性算法、128/256位完整性算法。 - **ZUC** - 祖冲之序列密码算法实现。使用SIMD、AES指令以及无进位乘法指令分别对**amd64**、**arm64**和**ppc64x**架构做了优化实现, 您也可以参考[ZUC实现及优化](https://github.com/emmansun/gmsm/wiki/Efficient-Software-Implementations-of-ZUC)和相关代码以获得更多实现细节。ZUC包实现了基于祖冲之序列密码算法的机密性算法、128/256位完整性算法。
- **CBCMAC** - 符合《GB/T 15852.1-2020 采用分组密码的机制》的消息鉴别码。 - **CBCMAC** - 符合《GB/T 15852.1-2020 采用分组密码的机制》的消息鉴别码。
- **CFCA** - CFCA中金特定实现目前实现的是SM2私钥、证书封装处理对应SADK中的**PKCS12_SM2**。 - **CFCA** - CFCA中金特定实现目前实现的是SM2私钥、证书封装处理对应SADK中的**PKCS12_SM2**信封加密、签名CSR生成及返回私钥解密、解析等功能
- **CIPHER** - ECB/CCM/XTS/HCTR/BC/OFBNLF加密模式实现。XTS模式同时支持NIST规范和国标 **GB/T 17964-2021**。当前的XTS模式由于实现了BlockMode其结构包含一个tweak数组所以其**不支持并发使用**。**分组链接BC模式**和**带非线性函数的输出反馈OFBNLF模式**为分组密码算法的工作模式标准**GB/T 17964**的遗留模式,**带泛杂凑函数的计数器HCTR模式**是**GB/T 17964-2021**中的新增模式。分组链接BC模式和CBC模式类似而带非线性函数的输出反馈OFBNLF模式的话从软件实现的角度来看基本没有性能优化的空间。 - **CIPHER** - ECB/CCM/XTS/HCTR/BC/OFBNLF加密模式实现。XTS模式同时支持NIST规范和国标 **GB/T 17964-2021**。当前的XTS模式由于实现了BlockMode其结构包含一个tweak数组所以其**不支持并发使用**。**分组链接BC模式**和**带非线性函数的输出反馈OFBNLF模式**为分组密码算法的工作模式标准**GB/T 17964**的遗留模式,**带泛杂凑函数的计数器HCTR模式**是**GB/T 17964-2021**中的新增模式。分组链接BC模式和CBC模式类似而带非线性函数的输出反馈OFBNLF模式的话从软件实现的角度来看基本没有性能优化的空间。
- **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日被归档的分支加入了商用密码支持。
@ -80,5 +82,5 @@ PKCS8包代码是[youmark/pkcs8](https://github.com/youmark/pkcs8)项目的一
使用本项目前,请务必仔细阅读[GMSM软件免责声明](DISCLAIMER.md) 使用本项目前,请务必仔细阅读[GMSM软件免责声明](DISCLAIMER.md)
## 关注度 ## 项目星标趋势
[![Stargazers over time](https://starchart.cc/emmansun/gmsm.svg?variant=adaptive)](https://starchart.cc/emmansun/gmsm) [![Stargazers over time](https://starchart.cc/emmansun/gmsm.svg?variant=adaptive)](https://starchart.cc/emmansun/gmsm)

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 {

180
cfca/example_test.go Normal file
View File

@ -0,0 +1,180 @@
package cfca_test
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"fmt"
"github.com/emmansun/gmsm/cfca"
"github.com/emmansun/gmsm/sm2"
)
func ExampleParseSM2() {
base64data := `
MIIDSQIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDDkLvKllj9ZWhaKU6MSnxBBV5yaF3tEcOk1
vQniWyVzyaQA4F3j/YvDJwEoE8gOF/swggL5BgoqgRzPVQYBBAIBBIIC6TCCAuUwggKJoAMCAQIC
BRBAmQgJMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0Ex
MB4XDTIwMTExOTA4MzExOFoXDTI1MTExOTA4MzExOFowgYkxCzAJBgNVBAYTAkNOMRcwFQYDVQQK
DA5DRkNBIFRFU1QgT0NBMTENMAsGA1UECwwEUFNCQzEZMBcGA1UECwwQT3JnYW5pemF0aW9uYWwt
MjE3MDUGA1UEAwwuMDUxQOmCruWCqOe6v+S4iuaUtuWNleWVhuaIt0BONTEwMTEzMDAwMTg4NzhA
MTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABJVRC63OKfcL4H324rDOdb4SSlbAjoJDXnK0qmwX
Z59FWmiSqt3ipreljKew4QynjTgR/yfp9yjNgNU8G5pkYdujggEGMIIBAjAfBgNVHSMEGDAWgBRr
/hjaj0I6prhtsy6Igzo0osEw4TAMBgNVHRMBAf8EAjAAMEgGA1UdIARBMD8wPQYIYIEchu8qAQEw
MTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wOQYDVR0f
BDIwMDAuoCygKoYoaHR0cDovL3VjcmwuY2ZjYS5jb20uY24vU00yL2NybDE0MzU2LmNybDAOBgNV
HQ8BAf8EBAMCBsAwHQYDVR0OBBYEFPiGPZT0oTuRXvkyGoOgviNEWnc1MB0GA1UdJQQWMBQGCCsG
AQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0gAMEUCIQCJDSsVPfhr+gnDASMj5Syt+hxs
amHygPecjCLbcdFQQgIgSXC4musF5Fnj/CpNTqvk9+56FuINkATGS8xRh7kzKBE=`
password := []byte("123456")
data, err := base64.StdEncoding.DecodeString(base64data)
if err != nil {
panic(err)
}
priv, cert, err := cfca.ParseSM2(password, data)
if err != nil {
panic(err)
}
fmt.Printf("%x\n", priv.D.Bytes())
fmt.Printf("%v\n", cert.Issuer)
// Output: d3f24d61bb2816882b8474b778dd7c3166d665f9455dc9d551c989c161e76ab0
// CN=CFCA TEST SM2 OCA1,O=China Financial Certification Authority,C=CN
}
func ExampleParseSM2_pemEncoded() {
pemdata := `
-----BEGIN CFCA KEY-----
MIIDSQIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDDkLvKllj9ZWhaKU6MSnxBBV5yaF3tEcOk1
vQniWyVzyaQA4F3j/YvDJwEoE8gOF/swggL5BgoqgRzPVQYBBAIBBIIC6TCCAuUwggKJoAMCAQIC
BRBAmQgJMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0Ex
MB4XDTIwMTExOTA4MzExOFoXDTI1MTExOTA4MzExOFowgYkxCzAJBgNVBAYTAkNOMRcwFQYDVQQK
DA5DRkNBIFRFU1QgT0NBMTENMAsGA1UECwwEUFNCQzEZMBcGA1UECwwQT3JnYW5pemF0aW9uYWwt
MjE3MDUGA1UEAwwuMDUxQOmCruWCqOe6v+S4iuaUtuWNleWVhuaIt0BONTEwMTEzMDAwMTg4NzhA
MTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABJVRC63OKfcL4H324rDOdb4SSlbAjoJDXnK0qmwX
Z59FWmiSqt3ipreljKew4QynjTgR/yfp9yjNgNU8G5pkYdujggEGMIIBAjAfBgNVHSMEGDAWgBRr
/hjaj0I6prhtsy6Igzo0osEw4TAMBgNVHRMBAf8EAjAAMEgGA1UdIARBMD8wPQYIYIEchu8qAQEw
MTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wOQYDVR0f
BDIwMDAuoCygKoYoaHR0cDovL3VjcmwuY2ZjYS5jb20uY24vU00yL2NybDE0MzU2LmNybDAOBgNV
HQ8BAf8EBAMCBsAwHQYDVR0OBBYEFPiGPZT0oTuRXvkyGoOgviNEWnc1MB0GA1UdJQQWMBQGCCsG
AQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0gAMEUCIQCJDSsVPfhr+gnDASMj5Syt+hxs
amHygPecjCLbcdFQQgIgSXC4musF5Fnj/CpNTqvk9+56FuINkATGS8xRh7kzKBE=
-----END CFCA KEY-----`
password := []byte("123456")
block, _ := pem.Decode([]byte(pemdata))
if block == nil {
panic("failed to decode PEM block")
}
priv, cert, err := cfca.ParseSM2(password, block.Bytes)
if err != nil {
panic(err)
}
fmt.Printf("%x\n", priv.D.Bytes())
fmt.Printf("%v\n", cert.Issuer)
// Output: d3f24d61bb2816882b8474b778dd7c3166d665f9455dc9d551c989c161e76ab0
// CN=CFCA TEST SM2 OCA1,O=China Financial Certification Authority,C=CN
}
func ExampleMarshalSM2_changePassword() {
pemdata := `
-----BEGIN CFCA KEY-----
MIIDSQIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDDkLvKllj9ZWhaKU6MSnxBBV5yaF3tEcOk1
vQniWyVzyaQA4F3j/YvDJwEoE8gOF/swggL5BgoqgRzPVQYBBAIBBIIC6TCCAuUwggKJoAMCAQIC
BRBAmQgJMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBURVNUIFNNMiBPQ0Ex
MB4XDTIwMTExOTA4MzExOFoXDTI1MTExOTA4MzExOFowgYkxCzAJBgNVBAYTAkNOMRcwFQYDVQQK
DA5DRkNBIFRFU1QgT0NBMTENMAsGA1UECwwEUFNCQzEZMBcGA1UECwwQT3JnYW5pemF0aW9uYWwt
MjE3MDUGA1UEAwwuMDUxQOmCruWCqOe6v+S4iuaUtuWNleWVhuaIt0BONTEwMTEzMDAwMTg4NzhA
MTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABJVRC63OKfcL4H324rDOdb4SSlbAjoJDXnK0qmwX
Z59FWmiSqt3ipreljKew4QynjTgR/yfp9yjNgNU8G5pkYdujggEGMIIBAjAfBgNVHSMEGDAWgBRr
/hjaj0I6prhtsy6Igzo0osEw4TAMBgNVHRMBAf8EAjAAMEgGA1UdIARBMD8wPQYIYIEchu8qAQEw
MTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wOQYDVR0f
BDIwMDAuoCygKoYoaHR0cDovL3VjcmwuY2ZjYS5jb20uY24vU00yL2NybDE0MzU2LmNybDAOBgNV
HQ8BAf8EBAMCBsAwHQYDVR0OBBYEFPiGPZT0oTuRXvkyGoOgviNEWnc1MB0GA1UdJQQWMBQGCCsG
AQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0gAMEUCIQCJDSsVPfhr+gnDASMj5Syt+hxs
amHygPecjCLbcdFQQgIgSXC4musF5Fnj/CpNTqvk9+56FuINkATGS8xRh7kzKBE=
-----END CFCA KEY-----`
password := []byte("123456")
block, _ := pem.Decode([]byte(pemdata))
if block == nil {
panic("failed to decode PEM block")
}
priv, cert, err := cfca.ParseSM2(password, block.Bytes)
if err != nil {
panic(err)
}
newpassword := []byte("654321")
data, err := cfca.MarshalSM2(newpassword, priv, cert)
if err != nil {
panic(err)
}
priv2, cert2, err := cfca.ParseSM2(newpassword, data)
if err != nil {
panic(err)
}
fmt.Printf("%x\n", priv2.D.Bytes())
fmt.Printf("%v\n", cert2.Issuer)
// Output: d3f24d61bb2816882b8474b778dd7c3166d665f9455dc9d551c989c161e76ab0
// CN=CFCA TEST SM2 OCA1,O=China Financial Certification Authority,C=CN
}
func ExampleCreateCertificateRequest() {
keyBytes, _ := hex.DecodeString("6c5a0a0b2eed3cbec3e4f1252bfe0e28c504a1c6bf1999eebb0af9ef0f8e6c85")
priv, err := sm2.NewPrivateKey(keyBytes)
if err != nil {
panic(err)
}
random := rand.Reader
// tmpKey used to decrypt the returned escrow PrivateKey
keyBytes, _ = hex.DecodeString("cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba")
tmpKey, err := sm2.NewPrivateKey(keyBytes)
if err != nil {
panic(err)
}
template := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: "certRequisition",
Organization: []string{"CFCA TEST CA"},
Country: []string{"CN"},
},
}
csrder, err := cfca.CreateCertificateRequest(random, template, priv, &tmpKey.PublicKey, "123456")
if err != nil {
panic(err)
}
// you can encode the csrder to PEM format or base64 format
csr, err := cfca.ParseCertificateRequest(csrder)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", csr.Subject)
fmt.Printf("%v\n", csr.ChallengePassword)
fmt.Printf("%x\n", csr.TmpPublicKey.(*ecdsa.PublicKey).X)
// Output: CN=certRequisition,O=CFCA TEST CA,C=CN
// 123456
// c7c4d0945ebdfc2111ad64b0e92e04582b0725fea172968c6c40162c810f8882
}
func ExampleParseEscrowPrivateKey() {
// a sample method to parse the escrow private key
keydata := `00000000000000010000000000000001000000000000000000000000000000000000000000000268MIHGAgEBBIHArhtKwTVT8dPEkykVRpvQNMxHv/yeqtaKZiSp2MbjcqMZtPfKW8IatiIPPitNhQtU5C7gMbsUxgf5Yo16vDSXdoWqoOOaes2pEJwmXWZI55lMMWc168WgzQ82fmMi05Vhlw9HNjGI3azE6MS5/ujSNGLZ0qAAmLnBiHlXFAXXAWRiy9MxZKwF4xKn6qMaKmkqbYmTbBbEJEhzJBmu0IJ1kNDcTFirAyapghHSw267erSUwsHjkQis9mKYpzGied0E`
keyBytes, _ := hex.DecodeString("cacece36cac24aab94e52bcd5c0f552c95028f2856053135a1e47510b4c307ba")
tmpKey, err := sm2.NewPrivateKey(keyBytes)
if err != nil {
panic(err)
}
encKey, err := cfca.ParseEscrowPrivateKey(tmpKey, []byte(keydata))
if err != nil {
panic(err)
}
fmt.Printf("%x\n", encKey.D.Bytes())
// Output: f6e02c941a0dfdac58d8b3b1bc1bd136f179741b7465ebc7b0b25bb381840a3b
}

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

@ -1,4 +1,4 @@
// Package cfca handles cfca issued key and certificate // Package cfca supports part of CFCA SADK's functions, provides interoperability with CFCA SADK.
package cfca package cfca
import ( import (
@ -13,22 +13,23 @@ import (
"github.com/emmansun/gmsm/smx509" "github.com/emmansun/gmsm/smx509"
) )
// CFCA私有格式在SADK中把它定义为PKCS12_SM2 // cfcaKeyPairData represents a key pair data structure used
// in CFCA (China Financial Certification Authority)
// for both parsing and marshaling SM2 keys and certificates.
type cfcaKeyPairData struct { type cfcaKeyPairData struct {
Version int `asn1:"default:1"` Version int `asn1:"default:1"`
EncryptedKey keyData EncryptedKey keyData
Certificate certData Certificate certData
} }
// 被加密的私钥数据 // Encrypted private key data
type keyData struct { type keyData struct {
ContentType asn1.ObjectIdentifier ContentType asn1.ObjectIdentifier
Algorithm asn1.ObjectIdentifier Algorithm asn1.ObjectIdentifier
EncryptedContent asn1.RawValue EncryptedContent asn1.RawValue
} }
// 对应的证书 // Corresponding certificate
type certData struct { type certData struct {
ContentType asn1.ObjectIdentifier ContentType asn1.ObjectIdentifier
Content asn1.RawContent Content asn1.RawContent
@ -41,6 +42,7 @@ var (
) )
// ParseSM2 parses the der data, returns private key and related certificate, it's CFCA private structure. // ParseSM2 parses the der data, returns private key and related certificate, it's CFCA private structure.
// This method is corresponding to CFCA SADK's cfca.sadk.asn1.pkcs.load.
func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, error) { func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, error) {
var keys cfcaKeyPairData var keys cfcaKeyPairData
if _, err := asn1.Unmarshal(data, &keys); err != nil { if _, err := asn1.Unmarshal(data, &keys); err != nil {
@ -57,7 +59,7 @@ func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, erro
} }
pk, err := DecryptBySM4CBC(keys.EncryptedKey.EncryptedContent.Bytes, password) pk, err := DecryptBySM4CBC(keys.EncryptedKey.EncryptedContent.Bytes, password)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, fmt.Errorf("cfca: failed to decrypt by SM4-CBC, please ensure the password is correct: %v", err)
} }
prvKey, err := sm2.NewPrivateKeyFromInt(new(big.Int).SetBytes(pk)) prvKey, err := sm2.NewPrivateKeyFromInt(new(big.Int).SetBytes(pk))
if err != nil { if err != nil {
@ -74,7 +76,8 @@ func ParseSM2(password, data []byte) (*sm2.PrivateKey, *smx509.Certificate, erro
return prvKey, cert, nil return prvKey, cert, nil
} }
// MarshalSM2 encodes sm2 private key and related certificate to cfca defined format // MarshalSM2 encodes sm2 private key and related certificate to cfca defined format.
// This method is corresponding to CFCA SADK's cfca.sadk.asn1.pkcs.CombineSM2Data.
func MarshalSM2(password []byte, key *sm2.PrivateKey, cert *smx509.Certificate) ([]byte, error) { func MarshalSM2(password []byte, key *sm2.PrivateKey, cert *smx509.Certificate) ([]byte, error) {
var err error var err error
var ciphertext []byte var ciphertext []byte

View File

@ -58,9 +58,57 @@ func parseTestKeyAndCert() (*sm2.PrivateKey, *smx509.Certificate, error) {
} }
func TestParseSM2(t *testing.T) { func TestParseSM2(t *testing.T) {
_, _, err := parseTestKeyAndCert() cases := []struct {
if err != nil { pem string
t.Fatal(err) password []byte
}{
{
v2exKeyPem,
[]byte("123456"),
},
{
`-----BEGIN CFCA KEY-----
MIIDmwIBATBHBgoqgRzPVQYBBAIBBgcqgRzPVQFoBDAjEsMB1LZrH4B5zBJQLh/S3vLTegY5twIU
lKu80vkB3XLfImABwhYVzFkjfJY1lWEwggNLBgoqgRzPVQYBBAIBBIIDOzCCAzcwggLaoAMCAQIC
BUQmAVGGMAwGCCqBHM9VAYN1BQAwXDELMAkGA1UEBhMCQ04xMDAuBgNVBAoMJ0NoaW5hIEZpbmFu
Y2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEbMBkGA1UEAwwSQ0ZDQSBBQ1MgU00yIE9DQTMx
MB4XDTIxMDcyMTA5NTMxMloXDTIxMDkyMTA5NTMxMlowbDELMAkGA1UEBhMCQ04xEzARBgNVBAoM
CkNGQ0EgT0NBMzExDzANBgNVBAsMBnlzZXBheTEVMBMGA1UECwwMSW5kaXZpZHVhbC0xMSAwHgYD
VQQDDBcwNTFAdGVzdF9zbTJAdGVzdF9zbTJAMjBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABIex
X8bD+NRAEyP9mKl8/OKHHfogP82NobcifE9zyFlH0MPyMyXnjMT4FBQ1HPGRTExIUvnnS1GnuG0E
gtF58oCjggF1MIIBcTBsBggrBgEFBQcBAQRgMF4wKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmNm
Y2EuY29tLmNuL29jc3AwMgYIKwYBBQUHMAKGJmh0dHA6Ly9jcmwuY2ZjYS5jb20uY24vb2NhMzEv
b2NhMzEuY2VyMB8GA1UdIwQYMBaAFAjY0SbESH2c7KyY6fF/YrmAzqlFMAkGA1UdEwQCMAAwSAYD
VR0gBEEwPzA9BghggRyG7yoBBDAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2EuY29tLmNu
L3VzL3VzLTE0Lmh0bTA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLmNmY2EuY29tLmNuL29j
YTMxL1NNMi9jcmwxNDQwLmNybDAOBgNVHQ8BAf8EBAMCBsAwHQYDVR0OBBYEFJDoMEr89lXvtODi
obIvu3LOpoiFMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAMBggqgRzPVQGDdQUAA0kA
MEYCIQCV2YwNr90ad1E5mZqzmdkU0E1CWie9K0lsml012slavgIhAM/++u/l1x5cCIPZsCOYrIy2
0N8+aiLInpgEnkw3wQMt
-----END CFCA KEY-----
`,
[]byte("ys123456"),
},
}
for _, c := range cases {
block, _ := pem.Decode([]byte(c.pem))
if block == nil {
t.Fatal("failed to decode PEM block")
}
priv, cert, err := ParseSM2(c.password, block.Bytes)
if err != nil {
t.Fatal(err)
}
if priv == nil {
t.Fatal("failed to parse private key")
}
if cert == nil {
t.Fatal("failed to parse certificate")
}
if !priv.PublicKey.Equal(cert.PublicKey) {
t.Fatal("public key mismatch")
}
} }
} }
@ -115,8 +163,8 @@ func TestParseSM2WithInvalidPwd(t *testing.T) {
password := []byte("wrongpwd") password := []byte("wrongpwd")
der, _ := hex.DecodeString("308203490201013047060a2a811ccf55060104020106072a811ccf5501680430016ca58c339f32f5bbe2b0491d5087c9b5de0f5aef4b4d0726941a0c21d9795f51e802f2a1596cc909d1638067ca28cc308202f9060a2a811ccf550601040201048202e9308202e530820289a00302010202051040990809300c06082a811ccf550183750500305c310b300906035504061302434e3130302e060355040a0c274368696e612046696e616e6369616c2043657274696669636174696f6e20417574686f72697479311b301906035504030c1243464341205445535420534d32204f434131301e170d3230313131393038333131385a170d3235313131393038333131385a308189310b300906035504061302434e31173015060355040a0c0e434643412054455354204f434131310d300b060355040b0c045053424331193017060355040b0c104f7267616e697a6174696f6e616c2d323137303506035504030c2e30353140e982aee582a8e7babfe4b88ae694b6e58d95e59586e688b7404e353130313133303030313838373840313059301306072a8648ce3d020106082a811ccf5501822d0342000495510badce29f70be07df6e2b0ce75be124a56c08e82435e72b4aa6c17679f455a6892aadde2a6b7a58ca7b0e10ca78d3811ff27e9f728cd80d53c1b9a6461dba382010630820102301f0603551d230418301680146bfe18da8f423aa6b86db32e88833a34a2c130e1300c0603551d130101ff0402300030480603551d200441303f303d060860811c86ef2a01013031302f06082b060105050702011623687474703a2f2f7777772e636663612e636f6d2e636e2f75732f75732d31342e68746d30390603551d1f04323030302ea02ca02a8628687474703a2f2f7563726c2e636663612e636f6d2e636e2f534d322f63726c31343335362e63726c300e0603551d0f0101ff0404030206c0301d0603551d0e04160414f8863d94f4a13b915ef9321a83a0be23445a7735301d0603551d250416301406082b0601050507030206082b06010505070304300c06082a811ccf5501837505000348003045022100890d2b153df86bfa09c3012323e52cadfa1c6c6a61f280f79c8c22db71d1504202204970b89aeb05e459e3fc2a4d4eabe4f7ee7a16e20d9004c64bcc5187b9332811") der, _ := hex.DecodeString("308203490201013047060a2a811ccf55060104020106072a811ccf5501680430016ca58c339f32f5bbe2b0491d5087c9b5de0f5aef4b4d0726941a0c21d9795f51e802f2a1596cc909d1638067ca28cc308202f9060a2a811ccf550601040201048202e9308202e530820289a00302010202051040990809300c06082a811ccf550183750500305c310b300906035504061302434e3130302e060355040a0c274368696e612046696e616e6369616c2043657274696669636174696f6e20417574686f72697479311b301906035504030c1243464341205445535420534d32204f434131301e170d3230313131393038333131385a170d3235313131393038333131385a308189310b300906035504061302434e31173015060355040a0c0e434643412054455354204f434131310d300b060355040b0c045053424331193017060355040b0c104f7267616e697a6174696f6e616c2d323137303506035504030c2e30353140e982aee582a8e7babfe4b88ae694b6e58d95e59586e688b7404e353130313133303030313838373840313059301306072a8648ce3d020106082a811ccf5501822d0342000495510badce29f70be07df6e2b0ce75be124a56c08e82435e72b4aa6c17679f455a6892aadde2a6b7a58ca7b0e10ca78d3811ff27e9f728cd80d53c1b9a6461dba382010630820102301f0603551d230418301680146bfe18da8f423aa6b86db32e88833a34a2c130e1300c0603551d130101ff0402300030480603551d200441303f303d060860811c86ef2a01013031302f06082b060105050702011623687474703a2f2f7777772e636663612e636f6d2e636e2f75732f75732d31342e68746d30390603551d1f04323030302ea02ca02a8628687474703a2f2f7563726c2e636663612e636f6d2e636e2f534d322f63726c31343335362e63726c300e0603551d0f0101ff0404030206c0301d0603551d0e04160414f8863d94f4a13b915ef9321a83a0be23445a7735301d0603551d250416301406082b0601050507030206082b06010505070304300c06082a811ccf5501837505000348003045022100890d2b153df86bfa09c3012323e52cadfa1c6c6a61f280f79c8c22db71d1504202204970b89aeb05e459e3fc2a4d4eabe4f7ee7a16e20d9004c64bcc5187b9332811")
_, _, err := ParseSM2(password, der) _, _, err := ParseSM2(password, der)
if err == nil || err.Error() != "padding: invalid padding byte/length" { if err == nil || err.Error() != "cfca: failed to decrypt by SM4-CBC, please ensure the password is correct: padding: invalid padding byte/length" {
t.Fatal("expected error of padding: invalid padding byte/length") t.Fatal("cfca: failed to decrypt by SM4-CBC, please ensure the password is correct: padding: invalid padding byte/length")
} }
} }

View File

@ -56,3 +56,28 @@ func VerifyMessageDetach(p7Der, sourceData []byte) error {
p7.Content = sourceData p7.Content = sourceData
return p7.Verify() return p7.Verify()
} }
// SignDigestDetach signs a given digest using the provided certificate and private key,
// and returns the detached PKCS7 signature.
//
// This method corresponds to CFCA SADK's cfca.sadk.util.p7SignByHash.
func SignDigestDetach(digest []byte, cert *smx509.Certificate, key crypto.PrivateKey) ([]byte, error) {
signData, _ := pkcs7.NewSMSignedDataWithDigest(digest)
if err := signData.SignWithoutAttr(cert, key, pkcs7.SignerInfoConfig{}); err != nil {
return nil, err
}
return signData.Finish()
}
// VerifyDigestDetach verifies a detached PKCS7 signature against a given digest.
// It parses the p7Der, assigns the provided digest to the parsed PKCS7 content, and then verifies it.
//
// This method corresponds to CFCA SADK's cfca.sadk.util.p7VerifyByHash.
func VerifyDigestDetach(p7Der, digest []byte) error {
p7, err := pkcs7.Parse(p7Der)
if err != nil {
return err
}
p7.Content = digest
return p7.VerifyAsDigest()
}

View File

@ -5,8 +5,11 @@
package cfca package cfca
import ( import (
"crypto/ecdsa"
"encoding/base64" "encoding/base64"
"testing" "testing"
"github.com/emmansun/gmsm/sm2"
) )
func TestSignMessageAttach(t *testing.T) { func TestSignMessageAttach(t *testing.T) {
@ -26,13 +29,12 @@ func TestSignMessageAttach(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
lastByte := p7[len(p7)-1] p7[0] = 0x20
p7[len(p7)-1] = 0
err = VerifyMessageAttach(p7) err = VerifyMessageAttach(p7)
if err == nil { if err == nil {
t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true) t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true)
} }
p7[len(p7)-1] = lastByte p7[0] = 0x30
err = VerifyMessageAttach(p7) err = VerifyMessageAttach(p7)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -62,13 +64,12 @@ func TestSignMessageDetach(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
lastByte := p7[len(p7)-1] p7[0] = 0x20
p7[len(p7)-1] = 0
err = VerifyMessageDetach(p7, []byte("test")) err = VerifyMessageDetach(p7, []byte("test"))
if err == nil { if err == nil {
t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true) t.Fatalf("VerifyMessageAttach() error = %v, wantErr %v", err, true)
} }
p7[len(p7)-1] = lastByte p7[0] = 0x30
err = VerifyMessageDetach(p7, []byte("test")) err = VerifyMessageDetach(p7, []byte("test"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -91,3 +92,31 @@ func TestSignMessageDetach(t *testing.T) {
var sadkSignedData = "MIICgAYKKoEcz1UGAQQCAqCCAnAwggJsAgEBMQ4wDAYIKoEcz1UBgxEFADAjBgoqgRzPVQYBBAIBoBUEE0hlbGxvIFNlY3JldCBXb3JsZCGgggGNMIIBiTCCAS+gAwIBAgIFAKncGpAwCgYIKoEcz1UBg3UwKTEQMA4GA1UEChMHQWNtZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTI0MTExOTAwMTIyNVoXDTI1MTExOTAwMTIyNlowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNub3cwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAATYcgrHXJmFO1/t/9WQ6GkCW6D0yDyd2ya5wRXjVAU08I9Oo6k99jB2MPauCn64W81APRCPHLlwWOtuIsmSmQhjo0gwRjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwQwHwYDVR0jBBgwFoAUyBfYaeGJxaf9ST9aCRgotC+MwvwwCgYIKoEcz1UBg3UDSAAwRQIgRaF0PA74cCYKeu8pZ4VDQti+rE283Hq/tGXzXUzOWKUCIQDl3z1boZxtRscbnOGOXg1NY+yoY2lz5b63kGOTkn/SxzGBoDCBnQIBATAyMCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyawIFAKncGpAwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARHMEUCIQCl145xtYc7QWTymATxUGbLfF1mlPlyMoIKSp9alu14UQIgQSV/Ll3yYCyXSNxhPelz8Nsbxopky+Pt56Al54rv3p0=" var sadkSignedData = "MIICgAYKKoEcz1UGAQQCAqCCAnAwggJsAgEBMQ4wDAYIKoEcz1UBgxEFADAjBgoqgRzPVQYBBAIBoBUEE0hlbGxvIFNlY3JldCBXb3JsZCGgggGNMIIBiTCCAS+gAwIBAgIFAKncGpAwCgYIKoEcz1UBg3UwKTEQMA4GA1UEChMHQWNtZSBDbzEVMBMGA1UEAxMMRWRkYXJkIFN0YXJrMB4XDTI0MTExOTAwMTIyNVoXDTI1MTExOTAwMTIyNlowJTEQMA4GA1UEChMHQWNtZSBDbzERMA8GA1UEAxMISm9uIFNub3cwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAATYcgrHXJmFO1/t/9WQ6GkCW6D0yDyd2ya5wRXjVAU08I9Oo6k99jB2MPauCn64W81APRCPHLlwWOtuIsmSmQhjo0gwRjAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwQwHwYDVR0jBBgwFoAUyBfYaeGJxaf9ST9aCRgotC+MwvwwCgYIKoEcz1UBg3UDSAAwRQIgRaF0PA74cCYKeu8pZ4VDQti+rE283Hq/tGXzXUzOWKUCIQDl3z1boZxtRscbnOGOXg1NY+yoY2lz5b63kGOTkn/SxzGBoDCBnQIBATAyMCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyawIFAKncGpAwDAYIKoEcz1UBgxEFADANBgkqgRzPVQGCLQEFAARHMEUCIQCl145xtYc7QWTymATxUGbLfF1mlPlyMoIKSp9alu14UQIgQSV/Ll3yYCyXSNxhPelz8Nsbxopky+Pt56Al54rv3p0="
var sadkSignedDataDetach = "MIICaQYKKoEcz1UGAQQCAqCCAlkwggJVAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIIBjTCCAYkwggEvoAMCAQICBQCp3BqQMAoGCCqBHM9VAYN1MCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0yNDExMTkwMDEyMjVaFw0yNTExMTkwMDEyMjZaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBTbm93MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE2HIKx1yZhTtf7f/VkOhpAlug9Mg8ndsmucEV41QFNPCPTqOpPfYwdjD2rgp+uFvNQD0Qjxy5cFjrbiLJkpkIY6NIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMEMB8GA1UdIwQYMBaAFMgX2GnhicWn/Uk/WgkYKLQvjML8MAoGCCqBHM9VAYN1A0gAMEUCIEWhdDwO+HAmCnrvKWeFQ0LYvqxNvNx6v7Rl811MzlilAiEA5d89W6GcbUbHG5zhjl4NTWPsqGNpc+W+t5Bjk5J/0scxgaAwgZ0CAQEwMjApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQCp3BqQMAwGCCqBHM9VAYMRBQAwDQYJKoEcz1UBgi0BBQAERzBFAiEA4ylCl8qQDfNDfBw7VkxVN0bUs4N56TZDqZhAdEv01N8CIDtOG5VbmWNZeagC8VRfzEhu+ratFCo3fTu2liV8kH5h" var sadkSignedDataDetach = "MIICaQYKKoEcz1UGAQQCAqCCAlkwggJVAgEBMQ4wDAYIKoEcz1UBgxEFADAMBgoqgRzPVQYBBAIBoIIBjTCCAYkwggEvoAMCAQICBQCp3BqQMAoGCCqBHM9VAYN1MCkxEDAOBgNVBAoTB0FjbWUgQ28xFTATBgNVBAMTDEVkZGFyZCBTdGFyazAeFw0yNDExMTkwMDEyMjVaFw0yNTExMTkwMDEyMjZaMCUxEDAOBgNVBAoTB0FjbWUgQ28xETAPBgNVBAMTCEpvbiBTbm93MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE2HIKx1yZhTtf7f/VkOhpAlug9Mg8ndsmucEV41QFNPCPTqOpPfYwdjD2rgp+uFvNQD0Qjxy5cFjrbiLJkpkIY6NIMEYwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMEMB8GA1UdIwQYMBaAFMgX2GnhicWn/Uk/WgkYKLQvjML8MAoGCCqBHM9VAYN1A0gAMEUCIEWhdDwO+HAmCnrvKWeFQ0LYvqxNvNx6v7Rl811MzlilAiEA5d89W6GcbUbHG5zhjl4NTWPsqGNpc+W+t5Bjk5J/0scxgaAwgZ0CAQEwMjApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxFZGRhcmQgU3RhcmsCBQCp3BqQMAwGCCqBHM9VAYMRBQAwDQYJKoEcz1UBgi0BBQAERzBFAiEA4ylCl8qQDfNDfBw7VkxVN0bUs4N56TZDqZhAdEv01N8CIDtOG5VbmWNZeagC8VRfzEhu+ratFCo3fTu2liV8kH5h"
func TestSignDigestDetach(t *testing.T) {
_, err := SignDigestDetach(nil, nil, nil)
if err == nil {
t.Fatalf("SignDigestDetach() error = %v, wantErr %v", err, true)
}
pair, err := createTestSM2Certificate(false)
if err != nil {
t.Fatal(err)
}
rawMessage := []byte("test")
digest, err := sm2.CalculateSM2Hash(pair.Certificate.PublicKey.(*ecdsa.PublicKey), rawMessage, nil)
if err != nil {
t.Fatal(err)
}
p7, err := SignDigestDetach(digest, pair.Certificate, pair.PrivateKey)
if err != nil {
t.Fatal(err)
}
err = VerifyDigestDetach(p7, digest)
if err != nil {
t.Fatal(err)
}
err = VerifyMessageDetach(p7, rawMessage)
if err != nil {
t.Fatal(err)
}
}

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

@ -10,6 +10,7 @@ SADKSecurity Application Development Kit是CFCA推出的一套支持全平
其JAVA版本(其它语言版本未知)基本上是一个基于[Bouncy Castle](https://www.bouncycastle.org/)的实现当然它看起来也支持JNI接入OpenSSL、U盾 其JAVA版本(其它语言版本未知)基本上是一个基于[Bouncy Castle](https://www.bouncycastle.org/)的实现当然它看起来也支持JNI接入OpenSSL、U盾
## 为什么会有互操作性问题 ## 为什么会有互操作性问题
* CFCA有一些实现没有相关标准。
* SADK存在较早可能有些实现早于标准发布。 * SADK存在较早可能有些实现早于标准发布。
* SADK版本较多不同版本也会有互操作性兼容问题。 * SADK版本较多不同版本也会有互操作性兼容问题。
* 其它未知原因。 * 其它未知原因。
@ -75,7 +76,7 @@ SADK 3.2之后的版本支持下列SM2密文格式(encryptedType)
1. 数据对称加密密钥的密文格式为**ASN.1编码格式**这个符合《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。 1. 数据对称加密密钥的密文格式为**ASN.1编码格式**这个符合《GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》。
2. SM4-CBC的OID使用了["SM4" block cipher](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104),而不是["SMS4-CBC"](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104.2)。 2. SM4-CBC的OID使用了["SM4" block cipher](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104),而不是["SMS4-CBC"](https://oid-rep.orange-labs.fr/get/1.2.156.10197.1.104.2)。
本软件库的```pkcs7.EncryptSM```方法```Decrypt```方法提供了SADK 3.2+版本的信封加解密兼容性记得cipher参数选择```pkcs.SM4```。```pkcs7.EncryptSM```方法符合GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》CFCA的SADK解密。 本软件库的```pkcs7.EncryptSM```方法```Decrypt```方法提供了SADK 3.2+版本的信封加解密兼容性。使用时,请确保`cipher`参数选择```pkcs.SM4```。```pkcs7.EncryptSM```方法符合GB/T 35275-2017 信息安全技术 SM2密码算法加密签名消息语法规范》CFCA的SADK可实现相应数据的解密。
本软件库的```pkcs7.EnvelopeMessageCFCA```方法提供了CFCA SADK更兼容的实现也就是recipientPolicyType=0。 本软件库的```pkcs7.EnvelopeMessageCFCA```方法提供了CFCA SADK更兼容的实现也就是recipientPolicyType=0。
@ -93,6 +94,8 @@ SADK 3.2之后的版本支持下列SM2密文格式(encryptedType)
* `cfca.VerifyMessageAttach` * `cfca.VerifyMessageAttach`
* `cfca.SignMessageDetach` * `cfca.SignMessageDetach`
* `cfca.VerifyMessageDetach` * `cfca.VerifyMessageDetach`
* `cfca.SignDigestDetach`
* `cfca.VerifyDigestDetach`
### 解密时自动检测? ### 解密时自动检测?
要穷举、尝试所有可能的密文格式不是不可以但这会或多或少地影响解密的性能。你要和对方集成还是知己知彼比较好对于加解密来说对用户透明不代表是好事。本软件库的SM2解密也实现了一定的自动检测通过首字节判断基于首字节只有固定那几个的假设 要穷举、尝试所有可能的密文格式不是不可以但这会或多或少地影响解密的性能。你要和对方集成还是知己知彼比较好对于加解密来说对用户透明不代表是好事。本软件库的SM2解密也实现了一定的自动检测通过首字节判断基于首字节只有固定那几个的假设
@ -101,8 +104,9 @@ 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私钥、证书的解析
这个是CFCA自定义的未见相关标准可以通过```cfca.ParseSM2```来解析。 这个是CFCA自定义的未见相关标准可以通过```cfca.ParseSM2```来解析。```cfca.ParseSM2```函数只接受**DER**编码的二进制数据,如果你的数据是**base64**编码的,请先自行解码。

View File

@ -40,18 +40,25 @@
### 签名数据Signed Data ### 签名数据Signed Data
签名数据,使用证书对应的私钥进行签名,理论上支持多个签名者,但通常使用场景都是单签。和数字信封数据类似,也分国密和非国密。 签名数据,使用证书对应的私钥进行签名,理论上支持多个签名者,但通常使用场景都是单签。和数字信封数据类似,也分国密和非国密。
#### 创建签名数据 #### 签名流程
1. 创建SignedData
是否国密是指OID也使用国密体系 是否国密是指OID也使用国密体系
| 是否国密 | 方法 | 默认签名算法 | | 是否国密 | 数据是否是哈希值 | 方法 | 默认签名算法 |
| :--- | :--- | :--- | | :--- | :--- | :--- | :--- |
| 否 | ```NewSignedData``` | SHA1 | | 否 | 否 | ```NewSignedData``` | SHA1 |
| 是 | ```NewSMSignedData``` | SM3 | | 否 | 是 | ```NewSignedDataWithDigest``` | SHA1 |
| 是 | 否 | ```NewSMSignedData``` | SM3 |
| 是 | 是 | ```NewSMSignedDataWithDigest``` | SM3 |
可选步骤:调用```SetDigestAlgorithm```设置想要的签名算法,通常国密不需要修改。 2. 可选步骤:调用```SetDigestAlgorithm```设置想要的签名算法,通常国密**不需要**修改。
接着调用```AddSigner```或```AddSignerChain```方法,进行签名;可以通过```SignerInfoConfig.SkipCertificates```指定忽略证书项(最终签名数据中不包含证书项); 3. 接着调用```AddSigner```或```AddSignerChain```方法,进行签名;可以通过```SignerInfoConfig.SkipCertificates```指定忽略证书项(最终签名数据中不包含证书项);
如果进行Detach签名则调用```Detach```方法; 4. 如果进行Detach签名则调用```Detach```方法;
最后调用```Finish```方法,序列化输出结果。 5. 最后调用```Finish```方法,序列化输出结果。
**注意**
1. 如果是直接对哈希值签名一定是Detach签名。
2. 国密签名如果要传入哈希值在有Attribute的情况下则哈希值只是标准的SM3哈希值否则必须是符合SM2签名标准的哈希值含SM2公钥信息
#### Detach签名 #### Detach签名
就是外部签名,**被签名数据**不包含在SignedData中也就是其ContentInfo.Content为空 就是外部签名,**被签名数据**不包含在SignedData中也就是其ContentInfo.Content为空
@ -86,7 +93,7 @@ if err := p7.VerifyWithChain(truststore); err != nil {
1. 调用```Parse```方法; 1. 调用```Parse```方法;
2. 如果是Detach签名数据则手动设置原始数据参考```testSign```方法); 2. 如果是Detach签名数据则手动设置原始数据参考```testSign```方法);
3. 如果签名数据中不包含证书项,则手动设置验签证书(参考```TestSkipCertificates``` 3. 如果签名数据中不包含证书项,则手动设置验签证书(参考```TestSkipCertificates```
4. 调用```Verify```或```VerifyWithChain```方法。 4. 如果Content是原始数据调用```Verify```或```VerifyWithChain```方法如果Content是哈希值调用```VerifyAsDigest```或```VerifyAsDigestWithChain```方法。
#### 特殊方法 #### 特殊方法
```DegenerateCertificate```退化成签名数据中只包含证书目前没有使用SM2 OID的方法如果需要可以请求添加。可以参考```TestDegenerateCertificate```和```TestParseSM2CertificateChain```。 ```DegenerateCertificate```退化成签名数据中只包含证书目前没有使用SM2 OID的方法如果需要可以请求添加。可以参考```TestDegenerateCertificate```和```TestParseSM2CertificateChain```。

View File

@ -186,6 +186,18 @@ func ExampleVerifyASN1WithSM2() {
} }
``` ```
### 如何处理不用Z的签名、验签
所谓**Z**就是用户可识别标识符和用户公钥、SM2椭圆曲线参数的杂凑值。其它签名算法如ECDSA是没有这个**Z**的这也是SM2签名算法难以融入以ECDSA签名算法为主的体系的主因。
#### 签名
也是使用sm2私钥的`Sign`方法,只是```SignerOpts```传入`nil`或者其它非`SM2SignerOption`即可,那么,你自己负责预先计算杂凑值,当然如何计算杂凑值,由你自己说了算了。
#### 验签
调用`sm2.VerifyASN1`方法,同样,你自己负责预先计算杂凑值,确保杂凑算法和签名时使用的杂凑算法保持一致。
### 如何对大文件签名、验签?
解决方案就是对杂凑值进行签名、验签。`sm2.CalculateSM2Hash`并不适合对大文件进行杂凑计算,请使用专门的`hash.Hash`接口实现。
## 密钥交换协议 ## 密钥交换协议
这里有两个实现一个是传统实现位于sm2包中另外一个参考最新go语言的实现在ecdh包中。在这里不详细介绍使用方法一般只有tls/tlcp才会用到普通应用通常不会涉及这一块感兴趣的话可以参考github.com/Trisia/gotlcp中的应用。 这里有两个实现一个是传统实现位于sm2包中另外一个参考最新go语言的实现在ecdh包中。在这里不详细介绍使用方法一般只有tls/tlcp才会用到普通应用通常不会涉及这一块感兴趣的话可以参考github.com/Trisia/gotlcp中的应用。
@ -260,29 +272,26 @@ func ExamplePrivateKey_Decrypt() {
我没有找到到哪个文档写有固定64字节转换的。从国家密码管理局2010年版的《SM2椭圆曲线公钥密码算法》到2012年的GM/T 0003-2012 SM2椭圆曲线公钥密码算法再到GB/T 32918-2016 信息安全技术 SM2椭圆曲线公钥密码算法都在第一部分第四章的“点到字节串的转换”有详细说明。这也符合《SEC 1: Elliptic Curve Cryptography》May 21, 2009 Version 2.0中2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion的描述。 我没有找到到哪个文档写有固定64字节转换的。从国家密码管理局2010年版的《SM2椭圆曲线公钥密码算法》到2012年的GM/T 0003-2012 SM2椭圆曲线公钥密码算法再到GB/T 32918-2016 信息安全技术 SM2椭圆曲线公钥密码算法都在第一部分第四章的“点到字节串的转换”有详细说明。这也符合《SEC 1: Elliptic Curve Cryptography》May 21, 2009 Version 2.0中2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion的描述。
#### 来自AI的问答 #### 来自AI的问答
**问: Why some crypto implementations of EC point to octet string conversion without form indicator?** **问: 为什么一些crypto实现中椭圆曲线点转换为八位字节串时没有使用格式指示符**
**答:** In Elliptic Curve Cryptography (ECC), the conversion of an elliptic curve point to an octet string can be done in several ways, typically either in compressed or uncompressed form. The form indicator (also known as the prefix) is used to denote whether the point is represented in its compressed or uncompressed form. **答:** 在椭圆曲线密码学ECC椭圆曲线点转换为八位字节串的方法有多种通常可以是压缩形式或未压缩形式。格式指示符也称为前缀用于表示该点是以压缩形式还是未压缩形式表示。
However, some implementations might choose to omit the form indicator for a few reasons: 然而,一些实现可能会出于几个原因选择省略格式指示符:
1. **Simplicity**: Omitting the form indicator simplifies the implementation, as there's no need to handle different forms of point representation. 1. **简洁性**:省略格式指示符可以简化实现过程,因为不需要处理不同形式的点表示。
2. **预设知识**:如果事先知道所有点都将处于某种特定形式(例如,所有点总是未压缩的),则可能会认为格式指示符是不必要的。
3. **兼容性**:并非所有系统或库都支持两种形式的点表示。为了确保最大兼容性,实现可能会选择始终使用一种形式,从而省略格式指示符。
4. **安全性**:在某些情况下,为了防止依赖于点形式的某些类型的攻击,可能会省略格式指示符。
2. **Assumed Knowledge**: If it's known in advance that all points will be in a certain form (e.g., all points are always uncompressed), the form indicator might be deemed unnecessary. 请注意,这些是通用原因,具体原因可能会根据确切的上下文以及使用的特定密码协议或系统而有所不同。
3. **Compatibility**: Not all systems or libraries support both forms of point representation. To ensure maximum compatibility, an implementation might choose to always use one form and thus omit the form indicator. **问:这不会引起互操作性问题吗?**
**答:** 是的,你说得对。在将椭圆曲线点转换为八位字节串时省略格式指示符,确实可能导致互操作性问题。
4. **Security**: In some cases, to prevent certain types of attacks that rely on the form of the point, the form indicator might be omitted. 当系统需要互操作时,它们必须就数据交换的通用格式达成一致。如果一个系统在编码椭圆曲线点时没有使用格式指示符,而另一个系统期望格式指示符存在,它们将无法正确解释彼此的数据。
Please note that these are general reasons and the specific reason can vary depending on the exact context and the specific cryptographic protocol or system in use. 这就是为什么像SEC高效密码学标准这样的标准会指定使用格式指示符。例如SEC 1标准指定未压缩点的格式指示符应为0x04压缩点的格式指示符应为0x02或0x03。
**问Won't this cause interoperability issues?** 因此,虽然在封闭系统中省略格式指示符可能会简化实现或提供其他好处,但它可能会阻碍与其他系统的互操作性。在编码和解码密码数据时,通常最好遵循已建立的标准,以确保最大的兼容性。
**答:** Yes, you're correct. Omitting the form indicator in the conversion of an elliptic curve point to an octet string can indeed cause interoperability issues.
When systems need to interoperate, they must agree on a common format for data exchange. If one system encodes elliptic curve points without a form indicator and another system expects the form indicator to be present, they won't be able to correctly interpret each other's data.
This is why standards like those defined by the SEC (Standards for Efficient Cryptography) specify the use of a form indicator. For example, the SEC 1 standard specifies that the form indicator should be 0x04 for uncompressed points and 0x02 or 0x03 for compressed points.
So, while omitting the form indicator might simplify the implementation or provide other benefits within a closed system, it can hinder interoperability with other systems. It's generally best to follow established standards when encoding and decoding cryptographic data to ensure maximum compatibility.
### 性能 ### 性能
从**v0.27.0**开始对大数据量的加解密做了优化处理尤其是KDF并行计算。详情请参考[SM2加解密性能](https://github.com/emmansun/gmsm/wiki/SM2%E5%8A%A0%E8%A7%A3%E5%AF%86%E6%80%A7%E8%83%BD)。 从**v0.27.0**开始对大数据量的加解密做了优化处理尤其是KDF并行计算。详情请参考[SM2加解密性能](https://github.com/emmansun/gmsm/wiki/SM2%E5%8A%A0%E8%A7%A3%E5%AF%86%E6%80%A7%E8%83%BD)。
@ -306,6 +315,26 @@ func calculateSM2Hash(pub *ecdsa.PublicKey, data, uid []byte) ([]byte, error) {
``` ```
公钥加密就没啥特殊只要确保输出密文的编码格式和KMS一致即可。 公钥加密就没啥特殊只要确保输出密文的编码格式和KMS一致即可。
## 基于密码硬件定制SM2私钥
密码硬件SDF/SKF中的用户密钥私钥通常是无法导出的但都提供了签名、解密APIs供调用为了和本软件库集成需要实现以下接口
1. `crypto.Signer`,这个接口的实现通常把传入的数据作为哈希值。
2. `crypto.Decrypter`,这个接口用于解密操作。
通常需要实现四个方法:
1. `Public() crypto.PublicKey`
2. `Sign(rand io.Reader, digest []byte, opts SignerOpts) (signature []byte, err error)`
3. `Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error)`
第一个返回公钥的方法是必须要实现的后面的方法取决于这个KEY的用途。
**注意**
1. `Sign(rand io.Reader, digest []byte, opts SignerOpts) (signature []byte, err error)`方法通常用于对哈希值作签名,最好遵从以下实现逻辑:检查`opts`是否是`*sm2.SM2SignerOption`类型,如果是,则把传入的`digest`作为原始数据进行处理,具体实现可以参考`sm2.SignASN1`函数。当然,在大多数情况下,直接将数据视为原始数据是可行的。实施者可以根据具体的应用场景灵活处理。
2. 如果密码硬件有自己的随机数源,可以忽略传入的`rand`
3. 很多设备签名函数通常只接收哈希值,需要调用```sm2.CalculateSM2Hash```或者**SDF**提供的哈希函数计算哈希值。
SDF API请参考《GB/T 36322-2018 密码设备应用接口规范》
## SM2扩展应用 ## SM2扩展应用
SM2的一些扩展应用譬如从签名中恢复公钥、半同态加密、环签名等大多尚处于POC状态也无相关标准。其它扩展应用但凡椭圆曲线公钥密码算法能用到的场合包括但不限于 SM2的一些扩展应用譬如从签名中恢复公钥、半同态加密、环签名等大多尚处于POC状态也无相关标准。其它扩展应用但凡椭圆曲线公钥密码算法能用到的场合包括但不限于
* [确定性签名](https://datatracker.ietf.org/doc/html/rfc6979) * [确定性签名](https://datatracker.ietf.org/doc/html/rfc6979)

View File

@ -324,3 +324,6 @@ SM9公钥加密算法支持多种对称加密算法不像SM2公钥加密算
## 密钥交换 ## 密钥交换
在这里不详细介绍使用方法一般只有tls/tlcp才会用到普通应用通常不会涉及这一块请参考[API Document](https://godoc.org/github.com/emmansun/gmsm)。 在这里不详细介绍使用方法一般只有tls/tlcp才会用到普通应用通常不会涉及这一块请参考[API Document](https://godoc.org/github.com/emmansun/gmsm)。
## 性能
参考[SM9实现及优化](https://github.com/emmansun/gmsm/wiki/SM9%E5%AE%9E%E7%8E%B0%E5%8F%8A%E4%BC%98%E5%8C%96)。

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

@ -32,8 +32,25 @@ func hexDecode(t *testing.T, s string) []byte {
return b return b
} }
func TestNewPrivateKeyWithOrderMinus1(t *testing.T) { func TestNewPrivateKey(t *testing.T) {
_, err := ecdh.P256().NewPrivateKey([]byte{ _, err := ecdh.P256().NewPrivateKey(nil)
if err == nil || err.Error() != "ecdh: invalid private key size" {
t.Errorf("ecdh: invalid private key size")
}
_, err = ecdh.P256().NewPrivateKey([]byte{
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b,
0x53, 0xbb, 0xf4, 0x09, 0x39, 0xd5, 0x41})
if err == nil || err.Error() != "ecdh: invalid private key size" {
t.Errorf("ecdh: invalid private key size")
}
allzero := make([]byte, 32)
_, err = ecdh.P256().NewPrivateKey(allzero)
if err == nil || err.Error() != "ecdh: invalid private key" {
t.Errorf("expected invalid private key")
}
_, err = ecdh.P256().NewPrivateKey([]byte{
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b, 0x72, 0x03, 0xdf, 0x6b, 0x21, 0xc6, 0x05, 0x2b,
@ -43,6 +60,18 @@ func TestNewPrivateKeyWithOrderMinus1(t *testing.T) {
} }
} }
func TestNewPublicKey(t *testing.T) {
_, err := ecdh.P256().NewPublicKey(nil)
if err == nil || err.Error() != "ecdh: invalid public key" {
t.Errorf("ecdh: invalid public key")
}
keydata := make([]byte, 65)
_, err = ecdh.P256().NewPublicKey(keydata)
if err == nil || err.Error() != "ecdh: invalid public key" {
t.Errorf("ecdh: invalid public key")
}
}
func TestECDH(t *testing.T) { func TestECDH(t *testing.T) {
aliceKey, err := ecdh.P256().GenerateKey(rand.Reader) aliceKey, err := ecdh.P256().GenerateKey(rand.Reader)
if err != nil { if err != nil {
@ -345,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.30.0
golang.org/x/sys v0.28.0 require golang.org/x/sys v0.32.0 // indirect
)

8
go.sum
View File

@ -1,4 +1,4 @@
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.28.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,7 +1,9 @@
package cpuid package cpuid
import "golang.org/x/sys/cpu" import "github.com/emmansun/gmsm/internal/deps/cpu"
var HasAES = cpu.X86.HasAES var (
var HasGFMUL = cpu.X86.HasPCLMULQDQ HasAES = cpu.X86.HasAES
var HasVPMSUMD = false HasGFMUL = cpu.X86.HasPCLMULQDQ
HasVPMSUMD = false
)

View File

@ -2,8 +2,10 @@
package cpuid package cpuid
import "golang.org/x/sys/cpu" import "github.com/emmansun/gmsm/internal/deps/cpu"
var HasAES = cpu.ARM64.HasAES var (
var HasGFMUL = cpu.ARM64.HasPMULL HasAES = cpu.ARM64.HasAES
var HasVPMSUMD = false HasGFMUL = cpu.ARM64.HasPMULL
HasVPMSUMD = false
)

View File

@ -6,6 +6,8 @@ package cpuid
// to detect their supported state dynamically. Assume the CPU features that // to detect their supported state dynamically. Assume the CPU features that
// Apple Silicon M1 supports to be available as a minimal set of features // Apple Silicon M1 supports to be available as a minimal set of features
// to all Go programs running on darwin/arm64. // to all Go programs running on darwin/arm64.
var HasAES = true var (
var HasGFMUL = true HasAES = true
var HasVPMSUMD = false HasGFMUL = true
HasVPMSUMD = false
)

View File

@ -2,6 +2,8 @@
package cpuid package cpuid
var HasAES = true var (
var HasGFMUL = false HasAES = true
var HasVPMSUMD = true HasGFMUL = false
HasVPMSUMD = true
)

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

@ -2,9 +2,14 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (ppc64 || ppc64le) && !purego //go:build !386 && !amd64 && !amd64p32 && !arm64
package subtle package cpu
//go:noescape func archInit() {
func xorBytes(dst, a, b *byte, n int) 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

@ -1,14 +1,15 @@
// Copyright 2018 The Go Authors. All rights reserved. // Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//
//go:build !purego
package subtle //go:build mips64 || mips64le
import "golang.org/x/sys/cpu" package cpu
var useAVX2 = cpu.X86.HasAVX2 const cacheLineSize = 32
//go:noescape func initOptions() {
func xorBytes(dst, a, b *byte, n int) options = []option{
{Name: "msa", Feature: &MIPS64X.HasMSA},
}
}

View File

@ -1,10 +1,11 @@
// Copyright 2018 The Go Authors. All rights reserved. // Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//
//go:build !purego
package subtle //go:build mips || mipsle
//go:noescape package cpu
func xorBytes(dst, a, b *byte, n int)
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)
}
}

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